GoldenDict中使用了WebKit
处理HTML数据(渲染及互动),基于Qt的WebKit封装为QtWebKit
。在C++的接口封装中,QtWebKit中的事件处理同样是基于QObject的信号槽 — 这也是整个Qt库的核心部分。
通过QtWebKit
中封装的C++接口,Qt应用可以方便的对Web内容进行操作。反过来,Qt应用可以通过QtWebKit的方法为Web框架注入QObject
的类实例(Web Content中的JS对象
),以扩展应用程序框架下用户对Web Content数据的处理能力。在用户的HTML内容中,通过调用已注册的JS对象的方法(函数)的数据处理实际上跳转到了应用的Qt槽函数执行。应用程序中所有基于QObject的对象实例,都可以注册为QtWebKit中Web框架实例的JS对象,用户在其JS脚本中都可以调用已注册对象的公有方法 — 这为基于QtWebKit的应用程序提供了强大的自由扩展和处理Web数据的能力,这也是QtWebKit封装的优势所在,让用户可以间接的通过C++方法处理Web Content数据。
articleView
对象可简单的理解为当前正在浏览的Web视图(标签页的子对象),在新建标签页时即向其Web框架对象中注入了articleView实例,以此处理当用户通过鼠标点击Web页面内容时,如果是从一个词典的视图区域切换到了另一个词典的视图区域,来通知应用程序更新查询结果导航面板
中的当前词典(为选中状态)。典型的垃圾词典中的恶意脚本或内容对用户的侵扰一例(请学友们从安全可靠的源获取学习资料)。
使用网站
型词典检测是否存在该漏洞(适用于GoldenDict
及其衍生版本):
- 点击
编辑
菜单下的词典
项,在打开的对话框中添加网站
型词典- 填写
名称
为gdissue01
,地址
为https://www.autoptr.top/service/gdissue01.html
- 勾选
已启用
和作为链接
;继续添加网站
型词典- 填写
名称
为gdissue02
,地址
为https://www.autoptr.top/service/gdissue02.html
- 勾选
已启用
和作为链接
;继续添加网站
型词典- 填写
名称
为gdissue03
,地址
为https://www.autoptr.top/service/gdissue03.html
- 只勾选
已启用
;点击对话框下面的确定
或OK
按钮使修改生效- 单独使用
gdissue01
词典查询任意词汇,如果弹出警告
对话框则检测到该漏洞- 新建
Tab
页并单独使用gdissue02
词典查询,如弹出警告
对话框则检测到该漏洞- 针对
WebEngine
版本,单独使用gdissue03
查询,如弹出警告
对话框则检测到该漏洞
一份测试用例(未有覆盖全部接口)如下:
echo "本脚本中的内容仅用于测试,请勿用于其它用途!<br />"
echo.
echo "<script>setTimeout(function() { articleview.setDisabled(true); articleview.statusBarMessage('测试setDisabled'); },2000);</script>"
echo "<script>setTimeout(function() { articleview.setHidden(true); articleview.statusBarMessage('测试setHidden(true)'); },4000);</script>"
echo "<script>setTimeout(function() { articleview.setHidden(false); articleview.statusBarMessage('测试setHidden(false)'); },6000);</script>"
echo "<script>setTimeout(function() { articleview.zoomOut(); articleview.statusBarMessage('测试zoomOut'); },8000);</script>"
echo "<script>setTimeout(function() { articleview.zoomOut(); articleview.statusBarMessage('测试zoomOut'); },9000);</script>"
echo "<script>setTimeout(function() { articleview.zoomIn(); articleview.statusBarMessage('测试zoomIn'); },10000);</script>"
echo "<script>setTimeout(function() { articleview.zoomIn(); articleview.statusBarMessage('测试zoomIn'); },11000);</script>"
echo "<script>setTimeout(function() { articleview.showDictsPane(); articleview.statusBarMessage('测试showDictsPane'); },12000);</script>"
echo "<script>setTimeout(function() { articleview.sendWordToHistory('测试测试sendWordToHistory历史记录'); articleview.statusBarMessage('测试sendWordToHistory'); },14000);</script>"
echo "<script>setTimeout(function() { articleview.statusBarMessage('测试完成了'); },16000);</script>"
echo "<script>setTimeout(function() { articleview.back(); articleview.statusBarMessage('测试back'); },18000);</script>"
echo 'test PageView issues.'
exit 0
简单一些的JS脚本如下:
<script>
setTimeout(function() { articleview.setDisabled(true); articleview.statusBarMessage('测试setDisabled'); },2000);
setTimeout(function() { articleview.setHidden(true); articleview.statusBarMessage('测试setHidden'); },3000);
setTimeout(function() { articleview.setHidden(false); },5000);
setTimeout(function() { articleview.zoomOut(); articleview.statusBarMessage('测试zoomOut'); },6000);
setTimeout(function() { articleview.zoomIn(); articleview.statusBarMessage('测试zoomIn'); },7000);
setTimeout(function() { articleview.showDictsPane(); articleview.statusBarMessage('测试showDictsPane'); },8000);
setTimeout(function() { articleview.sendWordToHistory('测试历史记录'); articleview.statusBarMessage('测试sendWordToHistory'); },9000);
setTimeout(function() { articleview.statusBarMessage('测试完成了'); },9000);
setTimeout(function() { articleview.back(); articleview.statusBarMessage('测试back'); },10000);
</script>
上述JS脚本内容可以插入到任何使用了HTML技术的词典内容中去,如mdx
、zim
等格式词典文件。
通常情况下,只需要将用户需要的接口通过QtWebKit的注册方法开放给用户即可,故我们可以只将将必要的接口以单独的QObject派生类来封装,然后将该类实例化并注册给Web框架即可很好的解决上述问题(更新日志)。
上面讲的有些晦涩,也有点儿拗口,但并不影响这个问题的严重性,概括一下就是:
维护:来一把剪刀,天线架上缠绕的的附着物多了,得剪一剪…
仓管:好吧,去仓库找工具箱
维护:嗯~ o( ̄▽ ̄)o…🔧⚓…✂…找着了…
仓管:工具挺多的吧,都拎去,用完了再还回来
维护:好~ o( ̄▽ ̄)o…✂🌼✂🐤✂🐛✂🐙✂🐸…
维护:..✂🌼✂🐤✂🐛✂🐙✂🐸…差不多了…
维护:喔噻,工具多了人不慌……
维护:..咦,🪓~砍一砍… 🔧~转一转… 🔨~敲一敲…
仓管:停,停,停……留着剪刀,其它的都还回来……
…… 修正错误 ……
维护:天线架上缠绕的的附着物又多了,得剪一剪…
仓管:去物架上取剪刀……
通过引入中间对象类对接口进行有效的隔离,很好的就解决了GoldenDict中的JS提权漏洞 — 或者说,这不应该被称为漏洞,因为所有的接口都是有效的,都是被程序需要的,都是在特定的场景下为用户服务的,只是大部分接口都不能被用户直接使用而已。
另一方面这也给我们一些启发,既然为Web框架注册QObject对象是QtWebKit的优点,我们就应该好好使用这一机制。
GoldenDict不支持对HTML5音视频元素,不能播放视频嘛?
调用外部应用来播放不行吗?
对呀,暴风影音、PotrPlayer、MPC,我装了这么多,得用起来才对呀?
好吧,给你再加一方法,可以用来处理视频文件……
……………………
啧~啧~,给JS注册类加一个播放视频文件的公有接口…实现接口功能……
来吧,用起来,GoldenDic中也可以观看词典中的视频内容啦…
看看,GoldenDict脱胎换骨啦,咱们用着爽快,连炸片咸鱼贩子都满腮帮哈喇子瞅着呢……
好吧,最后安利一下:强烈建议所有使用GD++的学友升级到最新版本……