如果访问令牌或者会话ID不容易凭人的直觉识别,可以使用自动化分析来帮助识别潜在的安全漏洞,本小节介绍自动分析可预测的会话ID和加密保护值的技术。
收集样本
分析服务器发出的会话ID的安全性和真正的随机性时,收集足够大的会话ID样本以便进行有意义的统计分析是必要的。你会希望使用脚本或者其他自动化工具(Burp sequencer工具是用于这个目的的极佳工具),因为手工快速收集10000个值非常乏味!下面有3个Perl脚本示例可以帮助你入门。你需要自定义每个脚本以收集特定的变量(为了演示的目的,我们在这些示例中使用一些COTS会话ID)。
下面的gather.sh脚本使用netcat从HTTP服务器收集ASPSESSIONID值:
#!/bin/sh # gather.sh while [ 1 ] do echo -e "GET / HTTP/1.0\n\n" | \ nc -vv $1 80 | \ grep ASPSESSIONID done
第二个脚本gather_ssl.sh使用openssl客户端从HTTPS服务器收集JSESSIONID值:
#!/bin/sh # gather_ssl.sh while [ 1 ] do echo -e "GET / HTTP/1.0\n\n" | \ openssl s_client -quiet -no_tls1 -connect $1:443 2>/dev/null | \ grep JSESSIONID done
最后一个脚本gather_nudge.sh使用openssl客户端从HTTPS服务器收集JSESSIONID值,而且发出一个服务器在设置cookie之前必需的特殊登录请求:
#!/bin/sh # gather_nudge.sh while [ 1 ] do cat nudge \ openssl s_client -quiet -no_tls1 -connect $1:443 2>/dev/null | \ grep JSESSIONID done
这个脚本中引用的“nudge”文件的内容如下:
POST /secure/client.asp?id=9898 HTTP/1.1 Accept: */* Content-Type: text/xml Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; Q312461) Host: www.victim.com Content-Length: 102 Connection: Keep-Alive Cache-Control: no-cache <LoginRequest><User><SignInName>latour</SignInName><Password>Eiffel </Password></User></LoginRequest>
每个脚本都运行于一个无限的循环中。确保重定向输出到一个文件,这样你可以保存工作成果。例如:
$ ./gather.sh www.victim.com | tee cookies.txt $ ./gather_ssl.sh www.victim.com | tee cookies.txt $ ./gather_nudge.sh www.victim.com | tee cookies.txt
提示 使用GNU cut命令和grep从cookies.txt中分析出实际的值。
非线性分析
如何测试一组会话ID的实际随机性?2001年4月,Bindview团队的Michal Zalewski对TCP连接的初始顺序号应用非线性分析技术,对这些值的“随机性”进行了饶有兴趣的观察。论文中最有说服力的部分是分析的图形表示。图5-5和5-6显示了两个来源的相对随机特性的视觉差异。
图5-5 适当随机化的ISN值
图5-6 质量不佳的随机化ISN值
ISN应该是用于每个新的TCP连接的随机数,和Web服务器生成的会话ID类似。用于生成这些图表的功能不需要任何复杂的算法。每个坐标定义如下:
x[t] = seq[t] - seq[t-1] y[t] = seq[t-1] - seq[t-2] z[t] = seq[t-2] - seq[t-3]
从数据集中选择的随机值是seq数组;t是数组的下标。试着把这种技术应用到从应用中收集的会话值。生成这种数据集实际上很简单。下面的Perl脚本接受一系列数字,计算每个点,并且(为了我们的目的)输出x,y,z:
#!/usr/bin/perl # seq.pl @seq = (); @x = @y = @z = (); while(<>) { chomp($val = $_); push(@seq, $val); } for ($i = 3; $i < $#seq; $i++) { push(@x, $seq[$i] - $seq[$i - 1]); push(@y, $seq[$i - 1] - $seq[$i - 2]); push(@z, $seq[$i - 2] - $seq[$i - 3]); } for ($i = 0; $i < $#seq; $i++) { print $x[$i] . " " . $y[$i] . " " . $z[$i] . "\n"; }
注意 这个函数不预测数值;它只是提示预测一个值的难度。质量不佳的生成器有可供利用的明显趋势。
为使用这个脚本,我们在session.raw文件中收集会话号码,然后通过管道将号码传送给Perl脚本,并将结果输出到数据文件3d.dat:
$ cat session.raw | ./seq.pl > 3d.dat
3d.dat文件的每一行包含一组X、Y、Z坐标。然后可以使用Gnuplot生成结果的图形表现。记住,虽然这个过程不能预测会话ID值,但是对于确定预测该值的难度很有帮助。
Burp web proxy的用户可能熟悉Sequencer(序列发生器)选项卡和内建的随机性统计分析工具。Sequencer实用工具不仅简化了令牌的收集,而且从服务器中的任何位置读取它们,随着令牌的读取自动进行随机性的数学分析。用请求/响应对填充Sequencer工具很简单,只要在Burp中的每个响应上单击右键,选择Send To Sequencer(发送到Sequencer)就行了。下一步是使用唯一的文本分隔符或者静态字节数定义令牌的界限。正确定义了令牌边界,令牌的收集和自动分析就可以开始了。有兴趣的读者可以参考Burp项目主网站(连接在本章结尾处的“参考与延伸阅读”小节中提供)。
暴力/字典攻击
在前面关于指纹识别的小节里,我们提到了MD5 hash的一些关键特性。如果你确定在应用会话cookie中发现了MD5 hash,可以使用经典的暴力猜测来确定原始的明文值(注意,虽然本节关注MD5,不过这些信息适用于任何散列算法)。
例如,下面的Perl命令使用Digest::MD5模块,取得不同的登录凭据组合并生成对应的MD5 hash值:
$ perl -e 'use Digest::MD5; \ > print Digest::MD5::md5_base64("userpasswd")' ZBzxQ5hVyDnyCZPUM89n+g $ perl -e 'use Digest::MD5; \ > print Digest::MD5::md5_base64("passwduser")' seV1fBcI3Zz2rORI1wiHkQ $ perl -e 'use Digest::MD5; \ > print Digest::MD5::md5_base64("passwdsalt")' PGXfdI2wvL2fNopFweHnyA
如果会话令牌匹配这些值,你就可以领会其生成的方法。尽管这个例子说明的是手工进行这个过程的方法,但是很容易开发自动化测试值生成以及与目标值比较的简单脚本。
使用MD5和其他散列算法的网站常常插入随机的数据或者其他动态生成的值来挫败这种暴力猜测攻击。例如,生成令牌的一种更安全(尤其是令牌基于用户密码时)的方式,包括了将密码与另一部分机密数据(常称为“盐”)和时间戳相连:
MD5( epoch time + secret + password )
在开头放置最动态的数据导致MD5更快“雪崩”。雪崩效应意味着两个仅仅略有不同的种子值将会生成两个大不相同的散列值。幸好恶意的用户仅仅拥有三段种子值中的一段。找到纪元时间的正确值也不太难(它只有100个可能取值),但是服务器的机密可能难以猜测。暴力攻击是可行的,但是只要正确地选取服务器机密值,攻击就很难成功。
仅使用服务器的机密和用户密码是安全性“较低”(“较高”和“较低”在加密中的定义很不明确)但同样可行的一种方法:
MD5( secret + password )
在这种情况下,攻击者将只需要猜测服务器的机密值就能够破解目标会话/授权令牌的生成方法。如果机密包含少数几个字符、是常用的密码、词典单词或者短语,那么成功的攻击是可以想见的。
对分析和领会会话/授权令牌值生成方式采取的方法也同样适用于加密值。
比特翻转(Bit Flipping)
这种攻击可能从留意一组加密值的趋势中得利。例如,你可能收集了一系列仅有某些部分不同的会话令牌:
46Vw8VtZCAvfqpSY3FOtMGbhI 4mHDFHDtyAvfqpSY3FOtMGbjV 4tqnoriSDAvfqpSY3FOtMGbgV 4zD8AEYhcAvfqpSY3FOtMGbm3
每个值都以数字4开始。如果这些是加密值,那么前导的4可能不是被加密的部分。在4之后是8个随机的字节,然后是14个不变的字节,最后是两个随机字节。如果这是一个加密字符串,那么我们可以对其内容进行有根据的猜测。我们假定它使用三重DES加密,因为DES公认比较脆弱:
字符串 = 固定数字 + 3DES(nonce) + 3DES(用户名 (+ 标志)) + 3DES(计数值) 4 8字节 14字节 2字节
以下是我们做出这个假设的原因:
·8个字符的部分总是变化。该值是加密的,所以我们无法知道它们是递增、递减还是真正随机的。不管怎么说,来源肯定是变化的,所以我们将其看作随机值。
·14个字节保持不变。这意味着加密数据来自于一个静态的来源,可能是用户名、名字或者“给我一个邮件提醒”的标志。
·最后两个字节未知。数据很短,所以我们可以猜测它只是一个计数器或者类似的值,会改变但不代表太多信息。它也可能是前面的数据的校验和,用于确保cookie没有遭到篡改。
用这些信息,攻击者可以进行“比特翻转”攻击:盲目地改变加密串的一部分,监视应用表现的变化。我们来看看cookie示例和三次修改:
原始: 4zD8AEYhcAvfqpSY3FOtMGbm3 修改 1: 4zD8AEYhcAAAAAAAAAAAAAAm3 修改 2: 4zD8AEYhcBvfqpSY3FOtMGbm3 修改 3: 4zD8AEYhcAvfqpSYAvfqpSYm3
我们将攻击集中在静态的14字节部分。第一种情况,尝试相似的字符。如果cookie在登录页面上被接受,那么我们知道服务器并不在这部分数据里检查验证凭据。如果cookie在查看用户简档的页面上被拒绝,我们可以猜测这个部分包含一些用户信息。
在第二种情况下,我们修改一个字符。现在我们必须提交cookie到应用的不同部分,看看它在哪里被接受,哪里被拒绝。它可能代表着用户和超级用户的一个标志,你永远不知道。(但是你可能会很幸运!)
第三种情况,我们重复字符串的前一半。格式可能是用户名:密码。如果做了这个改变,猜测结果是用户名:用户名,而登录页面拒绝,我们可能就已经走在正确的道路上了。这种方法有可能变成漫长、无尽的猜测。
如果需要帮助加解密的工具,可以尝试UNIX的crypt函数、Perl的Crypt::DES模块以及mcrypt程序库(http://mcrypt.hellug.gr/)。