我们已经准备好了,你呢?

2024我们与您携手共赢,为您的企业形象保驾护航!

周日晚上,有群突然发布消息,对宝塔面板存在未授权访问漏洞的紧急漏洞预警,并给出了大量存在漏洞的网址:

只需点击其中一个,您就会看到一个大的后端管理页面,无需任何身份验证或登录。当然,各种神奇的图片和神话后来在社交网络上流行起来。作为一个冷静的安全研究人员,我当然一笑置之,但我对这个漏洞的成因还是蛮感兴趣的,所以在这篇文章中我们将考察整个过程。事件发生的原因。

我们的问题到底是什么?

首先我先给出一个结论:这件事绝对不是单纯的pma目录我忘了删除,或者宝塔面板不小心配置错误,更不是某些人阴谋论中所说的官方的。故意留后门。

我为什么这么说呢?首先,根据官方的说法,该漏洞仅影响以下版本:

该版本是最新版本(bug修复版本)的前一个版本。也就是说,该小版本之前的版本面板不受影响。我们想一下,如果是“后门”或者是官方忘记删除的目录,为什么只影响这个版本呢?而且宝塔面板发展了这么久,已经积累了400万用户,系统安全性也比较成熟。如果真有这种低劣的错误或者“后门”,早就应该被发现了。

经过在网上实际查看案例并询问使用过宝塔面板的朋友,发现7.4.2之前的版本没有pma目录,并且认证方式默认需要输入账号密码。因此,如果这个漏洞出现在宝塔中,它一定做了以下两件事:

那么,我们的问题就变成了,官方为什么要做出这两项改变,目的是什么?

为了研究这个问题,我们需要先安装一个版本7.4.2。然而的安装是一个万无一失的一键脚本:

yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh

它不为用户提供选择版本号的选项。官方的Git可能已经很久没有更新了。如何安装合适的版本(7.4.2)?

安装合适的版本

当然这并不困扰我。首先,我使用上面提到的一键脚本安装了最新版本的宝塔面板。

安装过程自然是顺利的。安装完成后,系统显示的版本号是最新版本7.4.3,因为漏洞曝光后,官方很快修复并升级。不过没关系,我们还是可以找到离线升级包的:

它们分别是版本7.4.0/7.4.2/7.4.3。我们分别下载并解压,并尝试将我们的服务器版本恢复到存在漏洞的版本7.4.2。

在恢复代码之前,我们首先断开服务器与互联网的连接,或者将宝塔设置为离线模式:

php打开文件的五种方式_php打开软件_php要用什么打开

这样做的目的是为了防止宝塔进行自动版本更新,防止最后恢复的代码自动升级。

宝塔系统代码默认安装在/www//panel中。那么我们这里直接上传压缩包中的面板目录,覆盖已有的文件。重启宝塔,你会发现系统版本号已经恢复到7.4.2:

事情还没有结束。我们使用压缩包代码打开7.4.2和7.4.3看看官方是如何修复漏洞的:

php打开软件_php要用什么打开_php打开文件的五种方式

这是比较粗糙的。直接判断/www///pma目录是否存在。如果存在则直接删除。因此,虽然我们恢复了系统版本码,但是删除的pma已经不存在了,我们还是需要恢复这个目录。

方法也很简单。 /www//下有一个目录。我们可以直接复制这个目录:

php要用什么打开_php打开软件_php打开文件的五种方式

漏洞到底是什么?

环境搭建好了,我们还是要看代码。

首先,由于7.4.2是引入漏洞的版本,我们先看一下7.4.2官方的更新日志:

php打开文件的五种方式_php打开软件_php要用什么打开

打开7.4.0和7.4.2的压缩包代码,看看具体添加了哪些代码:

php打开软件_php要用什么打开_php打开文件的五种方式

可以看到7.4.2版本中增加了两个视图,分别对应 和 。 #start 方法在视图中使用。这个方法其实是新添加的:

    def start(self,puri,document_root,last_path = ''):
        '''
            @name 开始处理PHP请求
            @author hwliang<2020-07-11>
            @param puri string(URI地址)
            @return socket or Response
        '
''
        ...

        #如果是PHP文件
        if puri[-4:] == '.php':
            if  request.path.find('/phpmyadmin/') != -1:
                ...
                if request.method == 'POST':
                    #登录phpmyadmin
                    if puri in ['index.php','/index.php']:
                        content = public.url_encode(request.form.to_dict())
                        if not isinstance(content,bytes):
                            content = content.encode()
                        self.re_io = StringIO(content)
                        username = request.form.get('pma_username')
                        if username:
                            password = request.form.get('pma_password')
                            if not self.write_pma_passwd(username,password):
                                return Resp('未安装phpmyadmin')

                if puri in ['logout.php','/logout.php']:
                    self.write_pma_passwd(None,None)
            else:
                ...
           
        #如果是静态文件
        return send_file(filename)

代码太长我们就不分析了,只分析我写的部分。当请求的路径为//index.php且存在时,执行self.(,)。

自我跟进:

    def write_pma_passwd(self,username,password):
        '''
            @name 写入mysql帐号密码到配置文件
            @author hwliang<2020-07-13>
            @param username string(用户名)
            @param password string(密码)
            @return bool
        '
''

        self.check_phpmyadmin_phpversion()
        pconfig = 'cookie'
        if username:
            pconfig = 'config'
        pma_path = '/www/server/phpmyadmin/'
        pma_config_file = os.path.join(pma_path,'pma/config.inc.php')
        conf = public.readFile(pma_config_file)
        if not conf: return False
        rep = r"/\* Authentication type \*/(.|\n)+/\* Server parameters \*/"
        rstr = '''/* Authentication type */
$cfg['
Servers'][$i]['auth_type'] = '{}';
$cfg['
Servers'][$i]['host'] = 'localhost'; 
$cfg['
Servers'][$i]['port'] = '{}';
$cfg['
Servers'][$i]['user'] = '{}'; 
$cfg['
Servers'][$i]['password'] = '{}'; 
/* Server parameters */'
''.format(pconfig,self.get_mysql_port(),username,password)
        conf = re.sub(rep,rstr,conf)
        public.writeFile(pma_config_file,conf)
        return True

这段代码也很容易理解。如果传入 和 ,宝塔会重写配置文件.inc.php,更改认证方式,并记下账号密码。

这就是为什么pma在7.4.2版本可以直接访问的原因。

补个课:

支持多种身份验证方法。默认是身份验证。这时需要输入账户密码;用户还可以将认证方式更改为身份验证。此时将使用配置文件中的账户密码来连接MySQL数据库,即不需要输入账户密码。 。

采取这些行动的官方原因

事实上,读者看到这里一定会感到困惑。这些代码是什么意思?官方为何将认证方式改为mode?

这是很多漏洞分析文章中常见的问题。文章后面是漏洞代码,找到漏洞发生点和利用方法后结束。他们没有深入研究为什么开发要这样写。那么下次你仍然无法挖出这个漏洞。

那么,思考到这里,我们现在至少有以下几个问题:

对于第一个问题,其实我们可以简单地找到答案。正常安装最新版本的宝塔7.4.3时,当我们点击宝塔后台的链接时,会访问这样一个路径:

php打开软件_php要用什么打开_php打开文件的五种方式

为了修复这个漏洞,7.4.3版本回滚了部分代码,所以这个方法其实就是之前版本7.4.2的访问方式:通过888端口开头的文件夹直接访问。

在这种旧的访问方式中,888端口是一个单独的Nginx或服务器。整个过程是安全的,访问需要输入帐户和密码。

不过这种访问方式有点麻烦。它需要额外开放888端口,并且每次登录时都需要重新输入密码。因此,官方开发人员提出了一种新的方法,在宝塔后端级别将用户请求转发到php-fpm。这具有三个优点:

这就是为什么宝塔在7.4.2中添加了相关视图。这个视图是一个代理,它的工作就是将用户的请求转发给php-fpm。

当用户第一次使用这种方式登录时,系统会自动发送一个包含Mysql账户密码的数据包。此时宝塔后端会捕获账号密码,填写到配置文件中,并将认证方式改为。对于用户来说,体验是不再需要输入任何Mysql密码即可使用。

这确实给用户带来了更好的体验。

漏洞重现

说到这里我们应该还有一个疑问:既然官方的目的是“直接在层级上做用户认证,与宝塔的用户认证统一”,那么还有认证吗?为什么会出现未授权访问漏洞?

我们可以重现这个漏洞。首先我们以系统管理员身份登录宝塔后台,进入数据库页面,点击“”按钮,会弹出如下模式框:

php打开软件_php打开文件的五种方式_php要用什么打开

这里面有两种访问方式。 “通过Nginx//OIs访问”是老版本的访问方式,“通过面板安全访问”是7.4.2中新增加的代理模式。

我们点击“通过面板安全访问”并捕获数据包。我们将捕获这样一个数据包:

宝塔前端填写了我们的Mysql账号密码,直接发送。而且因为我们前面分析的代码,是在后台直接将账号和密码写入到配置文件中,实现免认证逻辑。

如果未认证的用户直接访问:8888//index.php怎么办?将直接重定向到登录页面:

如果只是这样的话,这个过程就不会有任何漏洞。然而,官方开发者犯了一个错误。他将pma应用程序放在/www//目录下,该目录原本是旧访问方式使用的Web根目录。

这意味着我可以通过旧的888端口+pma目录访问新的,而新的修改了官方的配置文件,最终导致了未授权访问漏洞:

php要用什么打开_php打开软件_php打开文件的五种方式

那么,如何解决这个问题呢?也很简单,把pma移到其他目录即可。

总结

我们来做个总结吧。

首先,宝塔面板绝对不是弱智。此漏洞不仅仅是将未经授权的 pma 留在外面并忘记删除它的问题。这实际上会打很多人的脸,因为大多数人认为这只是一个简单的未授权访问漏洞,并diss宝塔,但没想到背后其实存在复杂的逻辑错误。

其次,用户体验和安全绝对不冲突。我真的不喜欢为了确保安全而削弱用户体验的做法。所以希望宝塔官方不要因为这个漏洞事件而彻底回滚代码(据说7.4.3更新只是临时解决方案),需要改进的地方还是需要改进。

我已经好几年没有使用 Linux 面板了。这次重新体验了2020年的Linux面板,个人感觉宝塔其实是一个从外部更注重安全的系统,比如自动生成用户密码、用户名和密码。策略、默认的Php安全配置、自动版本更新等肯定比国内很多其他商业系统要好。但看代码,还是有很多需要改进的地方。稍后有机会我会详细阐述这一点。

php打开文件的五种方式_php要用什么打开_php打开软件

你喜欢这篇文章吗?关注我,走之前点击阅读~

二维码
扫一扫在手机端查看

本文链接:https://by928.com/6952.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线