今日惊闻Python出现了CVE,问题出在Lib/webbrowser.py模块,看描述还十分严重。

Python容易产生远程命令执行漏洞。攻击者可以利用此问题,在运行受影响应用程序的用户上下文中执行任意命令。失败的尝试可能导致拒绝服务条件。

根据CVE的描述,问题出在环境变量上。

Lib/webbrowser.py in Python through 3.6.3 does not validate strings before launching the program specified by the BROWSER environment variable, which might allow remote attackers to conduct argument-injection attacks via a crafted URL.

既然是BROWSER这个环境变量有问题,那我们就看下这个文件中哪里引用到了BROWSER这个环境变量,我这里测试的环境是Python3.6.3。在620行左右,发现了相关的代码。
image_1c1msknsq6f31f5jb1pqjp219.png-292.2kB

可以看到BROWSER环境变量经过一系列的处理,会传入register(name, klass, instance, update_tryorder)函数。我们继续跟入GenericBrowser(cmdline)

image_1c1msom6oh4u1vno1duu1us81fbim.png-431.7kB
跟入之后,发现cmdline被直接传入了subprocess.Popen()函数,问题应当就出在这里,经过简单的单步调试后,可以发现这个cmdline其实就是BROWSERurl拼接的列表,大概就是这个样子:cmdline: [BROWSER, url],既然如此,PoC就非常容易构造了。

import webbrowser


if __name__ == '__main__':
    webbrowser.open(".")

在运行之前先设定环境变量export BROWSER=ls,运行后即可发现命令执行了。
image_1c1mt2e771v4rqla15ka1joh3p613.png-19.5kB

分析到这里,漏洞基本上已经分析完了,我不禁多了一些思考。首先,如果我们想利用这个漏洞,那么需要控制环境变量以及一个url参数,然后还要这个服务开放在公网上才能被远程利用,从这一点上来看,这个漏洞怕是有些标题党了。

image_1c1mtbvo88p61c5ktbg139b12m92a.png-14.6kB

回过头来再从开发者的角度去思考一下,为啥要这样写代码,我觉得初衷可能是方便用户使用自定义的浏览器来打开URL(对这个webbrowser功能不熟悉的同学可以先看看官方文档的使用方法),所以势必要让用户来提供浏览器的名称,而开发者又无法预知用户会使用什么奇葩的浏览器,所以并没有办法进行有效的过滤。

image_1c1mtji671dna155col8v8n9862n.png-333.6kB

最后一处用户提供的URL部分,开发者也并没有判断这个URL是否合法,或者说最起码看起来是个正常URL的样子,传入一个.也是可以正常运行的,至少说来这个地方应该是不严谨的吧,挺好奇官方会如何修复这个问题的,也没准最后修都不修了呢23333

image_1c1mto69e2nh1ddo1bjvb7t1u9534.png-24.7kB