[此贴子已经被作者于2005-8-28 1:38:04编辑过]
QQ,就是OICQ,TENCENT公司研发的即时信息软件,是中国市场上国产IM软件绝对的老大。中国网民几乎人手至少一个QQ号码。大家都比我清楚,不多介绍。
本文谈谈QQ的安全问题。
QQ具有如此惊人的人气,却有着与之不相称的安全问题。基本上可以说,使用QQ,基本没有任何隐私可言!另外它也为你的电脑带来了诸多附送的安全隐患。有识之士如我都早已不用QQ啦。
一,本地密码保存方式
QQ的客户端会不经用户同意,把用户的密码经过数万次的MD5运算后存在本地。每次登陆在发送网络数据包之前进行本地验证,相信熟悉QQ的朋友对这一点都不陌生。这样事实上给了攻击者暴力破解QQ密码的机会,只要攻击者得到本地保存的这个数据即可。这个文件曾经叫ewh.db或者user.db,不知道现在是否又变了。说明一点,由于MD5作了数万次,这样的破解效率不高,但再低的效率也有弱智密码中招,当年在线尝试登陆都可以,还有什么不可能呢? 如果选择了自动登陆,那么密码的一次MD5保存在oicq2000.cfg中,破解速度大大提高。
二,本地聊天记录查看漏洞
典型的攻击场景是:已经拿到全部本地记录,就是一个以QQ号为名的文件夹,不知道密码(或者俗称忘记了密码,求助者多数号称是mm)如何查看其聊天记录? QQ登陆的流程是这样的:1.输入用户名密码->2.本地验证通过->3.得到用户界面(企鹅开始闪动)->4.发送登陆包->5.收到登陆包成功响应->6.登陆成功。 其中在步骤3的时候,我们就可以查看聊天记录了。 如果2失败,则要求重新输入密码;如果5失败,则退出步骤3中得到的用户界面。对于没有正确密码的攻击者来说,要保持3的状态以查看本地聊天记录,需要首先骗过2,并防止4发生。由于QQ没有软件加密(加壳),所以2十分容易,修改一个跳转就OK。4就更容易了,拔了网线或设置防火墙就可以了。现在网上有很多这样的修改教程和修改好的客户端。所以不再赘述。 三,本地聊天记录的加密方法 事实上以上第二条所说是一个利用已有客户端简单规避本地检查的办法。如果你愿意,可以自己写一个查看聊天记录的工具,因为它的加密方式是如此的脆弱。 首先普及一点常识:TEA算法(Tiny Encryption Algorithm,即微型加密算法)是一个正规的密码学意义上的加密算法,你可以在http://www.ftp.cl.cam.ac.uk/ftp/papers/djw-rmn/djw-rmn-tea.html找到原作者对其的介绍。QQ的加密,包括本地文件加密和远程数据包加密,主要使用的就是这个算法。值得注意的是原作者使用的是32轮运算(即使是32轮,这个算法也被证明是可以已知明文攻击的),然而TENCENT将其降为了16轮。时间上省了一半,但安全性却严重下降。算法常识以后再讲,我们只要知道该算法是公开的就可以了。(废话,不公开哪来的那么多开源QQ,比如lumaQQ)。 QQ本地历史数据,包括聊天记录和一些日志,用一个key加密后存在本地Msg.db或者MsgEx.db中,但是这个key呢,用QQ号码的MD5加密存在本地同一个文件中,换句话说,知道QQ号码就可以解密这些文件,QQ号码是什么呢?就是所在目录名! 当然,如果你启用了本地消息加密,情况会稍微好那么一点点,要看你的本地加密密码强度了。但是有多少人会去设置呢? 四,木马泛滥,Windows的HOOK 关于QQ密码的安全性我想是大家最关心的问题。网络上充斥着各种各样的偷QQ密码的文章或者工具,其实绝大多数越来越不好用,有些所谓偷密码的工具甚至本身就是木马,是来偷你自己的密码的。这些工具主要原理是挂钩子,判断当前窗口名,截键盘,某种方式发回。实际上,能种下木马本身已经是可遇而不可求的了。加上对方稍有安全意识,有个象样点的防火墙,这些工具就很难成功。 另外臭名昭著的QQ病毒之一类,俗称QQ尾巴的,也多是利用这种原理,查找窗口,发送消息。 针对这些问题,TENCENT公司自己也做了不少工作,比如窗口名是空的,实际看到的窗口名是画上去的。比如软键盘,比如其他的密码保护措施,可惜效果不太好啊。 五,登陆数据包如果遭窃听,密码可能被离线暴力破解 这是一个十分严重的问题,也会导致密码丢失。而且比前一条所说更严重。实际上,攻击者不再需要装木马,他只要会用Sniffer就可以了。 随便分析一下QQ的程序,或者看看开源的QQ程序,我们就知道,虽然普通数据包是协商的密钥,但QQ的登陆请求数据包中,由于还没有协商密钥,临时密钥是写在数据段之前的,也就是说这个数据包是可以轻松解密的。那么这个数据包里包含什么呢?它包含了用QQ密码的MD5或2次MD5加密空字符串得到的一段16字节数据!加密算法是TEA,最要命的是,经过TENCENT公司对算法模式的一个包装,如果用一个不相干的密钥去尝试解密这段二进制数据,解密函数会返回错误!而不是解密出一段无用的数据并返回正确!这种傻瓜逻辑为穷举攻击创造了极大的便利条件。实际攻击者不需要去穷举128位的密钥,他只要猜测可能的密码,做MD5运算一到二次后尝用来尝试解密那16字节数据就可以了。 简而言之,如果你的密码够简单,或者进了字典,那么恶意攻击者只要用一个sniffer,或者开个QQ代理,就能知道你的密码,这个穷举只要本地运算即可,而且速度非常之快。大约10^6~10^8次/秒。这个速度意味着,所有8位以下数字,6位以下字母,简单数字字母组合的密码都是形同虚设。作为对策,建议普通用户使用足够强壮的密码并且不要使用任何QQ代理。 六,得到工作密钥,实时解密QQ数据 QQ聊天数据加密用了协商密钥作工作密钥,这不错。可是,它竟然用对称算法传递协商的密钥!? QQ的工作密钥是服务器随机生成并发回的(也许不是随机?可以预测?不过我没搞到QQ服务器源码,不知道),如果QQ在线,这个密钥会是一直不变的,如果不在线一段时间,这个工作密钥就被更新。所以每两次登陆,如果间隔了一定时间,大约十几分钟,这个密钥就不同。从而一劳永逸地破解工作密钥是不现实的。但是如果有了QQ密码的MD5,那么攻击者可以解密QQ登陆确认数据包,解密后竟然就可以看到工作密钥,是16个字节的大小写字母加数字组合。此后所有客户端到服务器的数据将全部用此工作密钥解密。如果需要点对点聊天,那么所需密钥用同样算法和工作密钥加密送来。总之,此后所有QQ数据都可以认为是明文了。 如果你的密码被偷或被破解,攻击者很可能不通知你,也不修改你的密码,因为他知道你有密码保护,但是不要以为你的QQ号没有丢,可能攻击者正在看着你的聊天信息偷着乐呢。他也随时可以上线踢你下去,帮你聊天。 七,太多而繁杂的发行版本 QQ的客户端有太多的版本了,各种语言发行版不论,各种年号版、XP版、beta版、正式发行版,冠以各色名字,令人眼花缭乱,关键是他们还都好用。实际上,一个成功的C/S结构的网络产品不应当同时有太多的客户端版本,不仅难以维护,增大服务器开销,而且给冒名版本提供了可乘之机。现在流行有许多非官方发布的QQ客户端版本,什么珊瑚虫版,免广告版,显IP版……都大行其道,傻瓜也会想得到,其中必然有偷密码版,恶作剧版,留后门版在其中鱼目混珠。QQ对自己的客户端不加壳也就算了,它还不作进程检查和文件完整性校验,也没有守护进程。这么大大咧咧,怎么对得起这么多信之爱之的忠实用户啊? 八,多处不作任何加密处理的模块 也许由于数据量太大,语音和视频聊天就都没有加密,是明文传播,任何人可以直接在线收听收看。如果你想用WEB方式登陆www.qq.com,那么密码也就按照明文发出去了。 九,乱添插件,乱加功能 直接调用控件编写的内置IE浏览器,完全继承了IE的各种功能,以及漏洞。大家知道IE的漏洞研究的人十分之多,那么QQ也就随之非常危险了。 QQ秀,Q币,手机绑定等等在线服务,常常是每推出一个,就捎带出一堆漏洞。研究的人不少,这个需要在线研究,有风险,所以我没怎么干过。谁有经验告诉我啊。 自定义头像漏洞:据说QQ自定义头像的功能传出过一个漏洞,导致任意代码执行,我虽然没有实践过,但想来可能不假。 可以想见,功能越多,潜在的危险也就越大!这其实是安全软件开发中一条很有名的原则。所以建议各位,尽量少去玩试用版新功能,使用稳定而功能少的旧版本其实有时利大于弊。 十,相同认证方式的其他网络服务和产品 HTTP服务,《凯旋》游戏,QQ游戏大厅,等等,都可以用QQ号加同样的密码登陆。也许方便了用户,但在安全性上来讲这是大忌。整体安全性降到了其中的最小值。凯旋和游戏大厅是有客户端的,用户名密码的加密方式与QQ相同。但是HTTP可是明文的啊。 以下讲几个场景。 例如: 首先,滑稽的很,我竟然可以在http://service.qq.com查询到任意QQ号是否申请了密码保护,那么查查隔壁mm的吧。 然后,晓之以情动之以理,mm你去申请密码保护吧。同时打开sniffer。 哎,申请密码保护竟然要WEB方式输入密码!你知道么?申请密码保护的过程可能就是丢失密码的过程哦,顺便还捎带了提示问题及其答案呢! 再例如: 我是网管,已经得到了你的密码,我修改了你的密码,你没有密码保护,那么你可以去申诉,向一个CGI叫做http://service.qq.com/cgi-bin/TellError的申诉。 然后,我就知道了你的曾经密码,真实信息,另一个QQ号……真是大丰收啊。什么,你打电话申诉?好吧去打吧,很艰辛的,社会工程学攻击已经超出本篇的范围了。 再例如,你有密码保护,好吧那么你的找回过程还是要WEB登陆。再说,现在有多少电子邮箱又能抵挡住sniffer这样的简单工具呢?我就不多说了。
下面我们来欣赏一下QQ的解密代码。 void decrypt_qword(unsigned long *in, unsigned long *key, unsigned long *out) {
unsigned long code[4]; register unsigned long i=16, j=0xe3779B90, m, n;
m = swapu32(in[0]); n = swapu32(in[1]);
code[0] = swapu32(key[0]); code[1] = swapu32(key[1]); code[2] = swapu32(key[2]); code[3] = swapu32(key[3]);
while(i-- >0) { n -= ((m>>5)+code[3])^((m<<4)+code[2])^(j+m); m -= ((n>>5)+code[1])^((n<<4)+code[0])^(j+n); j += 0x61C88647; } out[0] = swapu32(m); out[1] = swapu32(n); }
int decrypt_msg(unsigned char *in, int inlen, unsigned long *key, unsigned char *out, unsigned long *outlen) { unsigned char q[8], mkey[8], *q1, *q2, *outp; register int count, i, j, p;
if (inlen%8 || inlen<16) return 0; decrypt_qword((unsigned long *)in, key, (unsigned long *)q); j = q[0]&0x7; count = inlen - j - 10; // if (*outlen < count || count < 0) return 0; //?????*outlen if (count < 0) return 0; *outlen = count;
memset(mkey, 0, 8); q2 = mkey; i = 8; p = 1; q1 = in+8; j ++; while (p <= 2) { if (j < 8) { j ++; p ++; } else if (j == 8) { q2 = in; for (j = 0; j < 8; j ++ ) { if (i + j >= inlen) return 0; q[j] ^= q1[j]; } decrypt_qword((unsigned long *)q, key, (unsigned long *) q); i += 8; q1 += 8; j = 0; } } outp = out; while(count !=0) { if (j < 8) { outp[0] = q2[j] ^ q[j]; outp ++; count --; j ++; } else if (j == 8) { q2 = q1-8; for (j = 0; j < 8; j ++ ) { if (i + j >= inlen) return 0; q[j] ^= q1[j]; } decrypt_qword((unsigned long *)q, key, (unsigned long *) q); i += 8; q1 += 8; j = 0; } } for (p = 1; p < 8; p ++) { if (j < 8) { if (q2[j]^q[j]) return 0; j ++; } else if (j == 8 ) { q2 = q1; for (j = 0; j < 8; j ++ ) { if (i + j >= inlen) return 0; q[j] ^= q1[j]; } decrypt_qword((unsigned long *)q, key, (unsigned long *) q); i += 8; q1 += 8; j = 0; } } return 1; }
算法模式简要概括如下: F(i) = P(i) + C(i-1) C(i) = E(Fi) + F(i-1) P是明文,C是密文,E是TEA算法,作用于8字节单元上。每次i增加1,就作用于一个8字节分组。
我们看到QQ的加密在算法模式上作了两个设计,其一引入随机字符作头部padding,这样基本保证同样的明文和密钥可以导致完全不同的加密结果。模式设计使得解密函数解密后可以准确抛弃掉这部分随机字。这个设计的确很好。但其模式在另一点上做的太差了,就是用返回值明确指出了解密的成功与否。
实际上至少两年前,水木清华的Crack版上,pure(青衣~ shadow in silence) 的文章就揭示了这一段代码,早已不是什么秘密了。我自己也跟踪得到了这样的东西,不过还是参考过Shufeng Tan的Net-OICQ-0.8,以及Puzzlebird为Gaim写的QQ插件OpenQ-0.3.1,至于lumaQQ就没看了,想来也就差不多了吧。 事实上我还有一个猜想,32轮的TEA降低到16轮,肯定是出奇地危险,如果对算法破解有兴趣,那么可以看看下面这些参考书。 1)John Kelsey,Bruce Schneier, David Wagner, “Related Cryptanalysis of 3-Way, Biham-DES, CAST, DES-X, NewDES, RC2, and TEA” 2)Fauzan Mirza, “Block Ciphers And Cryptanalysis” 3)VIKRAM REDDY ANDEM, “A CRYPTANALYSIS OF THE TINY ENCRYPTION ALGORITHM”
也许由于时间原因,技术能力所限,以上描述难免有偏颇不足或谬误之处,或者过时了。欢迎不吝赐教。doublelee[at.]etang.com 照规矩,该致谢了。谢谢老师,同学,领导,同事,老爸老妈,哎……可惜呀,要是偶有mm就可以一起谢谢了。
附加:
把你能正常登陆的QQ号下的ewh.db复制到要破解的qq号下,再把要破解下的ewh.db用16进制工具打开,例如:
00000000: 51 44 01 01 03 00 04 03 00 bd af a8 04 00 00 00 00000010: c9 6a 09 00 07 03 00 b9 ab b4 10 00 00 00 0f c5 00000020: e9 d4 31 15 2f 12 c4 1c 0a 46 95 90 db 98 04 03 00000030: 00 a9 b5 b2 04 00 00 00 69 f4 aa 02
最后4字节是qq号的16进制。 把你的qq号用附件中计算器换成16进制数替换最后4字节,断开网络,用要破解的qq号和你的密码就能登陆看到聊天记录了。