正常一次最多邀请40人进群
超过40人的拉群,会变成邀请,需要对方同意
新版本修复了漏洞,但还是可以用老版本进行强制拉群
虽然官方也做了版本过低的限制,但还是有办法绕过
要么修改版本号或者登录几天新版本,之后就可以登录老版本
关键是,新版本的修复漏洞的方案并不彻底
深入研究发现更严重的漏洞,最新版本也能强制拉群
而且这个漏洞是Duilib框架层面的,影响的不只是拉群老版本强制拉群漏洞
1、正常情况
点击主界面的添加群成员
会弹出一个选择用户的界面
再去点主界面的添加群成员
选择用户的界面还是同一个
2、异常情况
工作台-客户群-前往查看客户群-添加群成员
点多少次就会出现多少个选择用户的界面
分析漏洞背后的原因
父界面的句柄
企业微信的界面框架是Duilib
弹出子界面之后,还能点击父界面
第一反应是创建子界面没有传父界面的句柄
拦截创建界面的函数Create,发现都有传父界面的句柄
1、正常情况
2、异常情况
好玩的是,正常和异常情况的两个选择用户界面可以同时出现
因为"工作台-客户群-前往查看客户群"这一步,创造新界面的时候没有传父窗口句柄
调用堆栈
既然都传了父界面的句柄,那差异可能是代码逻辑不一样
继续拦截创建界面的函数Create,看调用堆栈有什么差异
1、正常情况
调用堆栈有DispatchMessageW,并且窗口事件是0x403
0x403代表的是自定义消息,说明创建子界面的异步的
2、异常情况
调用堆栈有WndProc,并且窗口事件是0x202
说明是鼠标弹起之后,直接同步地创建子界面
窗口属性
正常情况异步创建子界面,但父界面没响应
异常情况同步创建子界面,但父界面有响应
都有父窗口句柄,说明正常情况额外加了逻辑
用spy++看一下父界面窗口的样式属性
1、正常情况
创建子窗口之前
创建子窗口之后
多了WS_DISABLED,因此父界面没响应
2、异常情况
创建子窗口之前
创建子窗口之后
没有WS_DISABLED,因此父界面有响应
漏洞的原因就定位到了:异常情况,父窗口的样式因为没有WS_DISABLED导致能被点击多次
最新版本利用这个原理,正常情况点击添加群成员,代码去掉WS_DISABLED,可以被点击多次
没有WS_DISABLED是最终结果,好奇代码层面到底是哪里出问题
对SetWindowLong下断点,看下是不是异常情况少了这个逻辑
发现正常情况和异常情况都没有命中SetWindowLong这个函数
影响WS_DISABLED的API还有EnableWindow,继续下断点
确实正常情况会调用EnableWindow,异常情况则不会
EnableWindow对应在Duilib里面是ShowModal
问题就出在:异常情况没有调用ShowModal,导致父窗口没有锁定
但因为异常情况是同步创建子窗口,如果直接调用ShowModal会进入新的消息循环,同一线程的父窗口消息循环会被阻塞,自己维护一下EnableWindow会好一些
不过就像前面说的,正常情况有调用ShowModal,去掉WS_DISABLED还是能强制拉群
想要保证窗口的唯一性,还是得在逻辑代码上加判断,不能只是依赖Duilib框架本身的限制