宝塔面板phpMyAdmin未授权访问安全漏洞是个低级错误吗?
周日晚,某群里突然发布了一则消息,宝塔面板的phpmyadmin存在未授权访问漏洞的紧急漏洞预警,并给出了一大批存在漏洞的URL:
随便点开其中一个,赫然就是一个大大的phpmyadmin后台管理页面,无需任何认证与登录。当然,随后各种神图神事也都刷爆了社交网络,作为一个冷静安全研究者,我对此当然是一笑置之,但是这个漏洞的原因我还是颇感兴趣的,所以本文我们就来考证一下整件事情的缘由。
一、我们的问题究竟是什么?
首先,我先给出一个结论:这件事情绝对不是简简单单地有一个pma目录忘记删除了,或者宝塔面板疏忽大意进行了错误地配置,更不是像某些人阴谋论中说到的官方刻意留的后门。
我为什么这么说?首先,根据官方的说法,这个漏洞只影响如下版本:
Linux正式版7.4.2
Linux测试版7.5.13
Windows正式版6.8
这个版本就是最新版(漏洞修复版)的前一个版本。也就是说,这个确定的小版本之前的版本面板是不受影响的。我们试想一下,如果是“后门”或者官方忘记删除的目录,为什么只影响这一个版本呢?况且宝塔面板发展了这么久,积累了400万用户,体系安全性也相对比较成熟,如果存在这么低劣的错误或“后门”,也应该早就被发现了。
经过实际查看互联网上的案例和询问使用了宝塔面板的朋友,我发现在7.4.2以前的版本中没有pma这个目录,并且phpmyadmin默认情况下认证方法是需要输入账号密码的。所以,宝塔出现这个漏洞,一定是做过了下面这两件事:
新增了一个pma目录,内容phpmyadmin
phpmyadmin的配置文件被修改了认证方式
那么,我们的问题就变成了,官方为什么要做这两处修改,目的究竟是什么?
为了研究这个问题,我们需要先安装一个宝塔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,因为在爆出这个漏洞以后,官方迅速进行了修复升级。不过没关系,我们仍然可以找到离线升级包:
http://download.bt.cn/install/update/LinuxPanel-7.4.0.zip http://download.bt.cn/install/update/LinuxPanel-7.4.2.zip http://download.bt.cn/install/update/LinuxPanel-7.4.3.zip
分别是7.4.0/7.4.2/7.4.3的版本,我们分别下载并解压,并尝试将自己的服务器版本恢复成漏洞版本7.4.2。
在恢复代码之前,我们先将服务器断网,或者将宝塔设置成离线模式:
这么做的目的是防止宝塔进行自动版本更新,避免好不容易恢复的代码又自动升级了。
宝塔系统代码默认安装完是在/www/server/panel
,接着我们直接将将压缩包内的panel目录上传到这里来,覆盖掉已有的文件。重启下宝塔,即可发现系统版本号已经恢复成7.4.2了:
还没完,我们使用beyond compare打开7.4.2和7.4.3的压缩包代码,先看看官方是怎么修复的漏洞:
比较粗暴,直接判断目录/www/server/phpmyadmin/pma
是否存在,如果存在就直接删掉。所以,我们虽然恢复了系统版本代码,但删掉的pma已经不在了,我们还需要恢复一下这个目录。
方法也很简单,/www/server/phpmyadmin
下本身存在一个phpmyadmin目录,我们直接复制一下这个目录即可:
三、漏洞究竟是怎么回事
有了环境,我们仍需看看代码。
首先,由于7.4.2是引入漏洞的版本,我们看看官方对7.4.2的更新日志:
用beyond compare打开7.4.0和7.4.2的压缩包代码,看看具体增加了哪些代码:
可见,在7.4.2版本中增加了两个视图,分别对应着phpmyadmin和adminer。视图中用到了panelPHP#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)
代码太长,我们不展开分析,只我写出来的部分。在请求的路径是/phpmyadmin/index.php
且存在pma_username
、pma_password
时,则执行self.write_pma_passwd(username,password)
。
跟进self.write_pma_passwd:
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
这个代码也很好理解了,如果传入了username和password的情况下,宝塔会改写phpmyadmin的配置文件config.inc.php,将认证方式改成config
,并写死账号密码。
这就是为什么7.4.2版本中pma可以直接访问的原因。
补个课:
phpmyadmin支持数种认证方法,默认情况下是Cookie认证,此时需要输入账号密码;用户也可以将认证方式修改成Config认证,此时phpmyadmin会使用配置文件中的账号密码来连接mysql数据库,即不用再输入账号密码。
四、官方做这些动作的原因
其实各位看官看到这里肯定脑子里还是一团浆糊,这些代码究竟意味着什么呢?为什么官方要将认证模式改成config模式?
是很多漏洞分析文章的通病,这些文章在出现漏洞后跟一遍漏洞代码,找到漏洞发生点和利用方法就结束了,并没有深入研究开发为什么会这么写,那么下次你还是挖不出漏洞。
所以,这里思考一下,我们现在起码还有下列疑问:
在7.4.2版本以前,用户是如何使用phpmyadmin的?
宝塔为什么要在7.4.2版本增加phpmyadmin有关的视图?
宝塔为什么要将phpmyadmin认证模式改成config?
我们如何复现这个漏洞?
第一个问题,我们其实可以简单找到答案。在正常安装宝塔最新版7.4.3时,我们点击宝塔后台的phpmyadmin链接,会访问到这样一个路径:
7.4.3版本为了修复这个漏洞,回滚了部分代码,所以这种方式其实就是7.4.2以前版本的phpmyadmin的访问方式:通过888端口下的一个以phpmyadmin_
开头的文件夹直接访问phpmyadmin。
这种老的访问方法中,888端口是一个单独的Nginx或Apache服务器,整个东西是安全的,访问也需要输入账号密码。
但是这种访问方法有些麻烦,需要额外开放888端口,而且每次登陆都要重新输入密码。所以,官方开发人员提出了一种新的做法,在宝塔后端的python层面转发用户对phpmyadmin的请求给php-fpm。这样有三个好处:
直接在python层面做用户认证,和宝塔的用户认证进行统一,不需要多次输入mysql密码
也不需要再对外开放888端口了
使用phpmyadmin也不再依赖于Nginx/Apache等服务器中间件了
这就是为什么宝塔要在7.4.2增加phpmyadmin有关的视图的原因,这个视图就是一个phpmyadmin的代理,做的事情就是转发用户的请求给php-fpm。
用户在第一次使用这种方式登录时,系统会自动发送包含了Mysql账号密码的数据包,宝塔后端会捕捉到此时的账号密码,填入phpmyadmin的配置文件,并将认证方式改成config
。对于用户来说,感受到的体验就是,不再需要输入任何Mysql密码即可使用phpmyadmin了。
这的确给用户的使用带来了更好的体验。
五、漏洞复现
此时我们应该还有个疑问:既然官方目的是“直接在python层面做用户认证,和宝塔的用户认证进行统一”,那么仍然是有认证的呀?为什么会出现未授权访问漏洞呢?
我们可以来复现一下这个漏洞。首先,我们以系统管理员的身份登录宝塔后台,来到数据库页面,点击“phpMyAdmin”按钮,会弹出如下模态框:
这个里面有两种访问模式,“通过Nginx/Apache/OIs访问”是老版本的访问方式,“通过面板安全访问”就是7.4.2新增加的代理模式。
我们点击“通过面板安全访问”,并抓包,会抓到这样一个数据包:
宝塔前端将我们的Mysql账号密码填好了直接发给phpmyadmin。又因为我们前面分析过的那段代码,后台将账号密码直接写入了phpmyadmin配置文件,来做到免认证的逻辑。
如果一个未认证的用户,直接访问http://ip:8888/phpmyadmin/index.php
呢?会被直接重定向到登录页面:
如果仅仅是这样,这个过程是不存在漏洞的。但是,官方开发人员犯了一个错误,他将pma应用放在了/www/server/phpmyadmin
目录下,而这个目录原本是老的phpmyadmin访问方式所使用的Web根目录。
这意味着,我通过老的888端口+pma目录,可以访问到新的phpmyadmin,而新的phpmyadmin又被官方修改了配置文件,最终导致了未授权访问漏洞:
所以,如何解决这个问题呢?也很简单,只需要将pma移到其他目录去即可。
六、总结
我们来做个总结。
首先,宝塔面板绝对不是弱智,这个漏洞不是简简单单的放了一个未授权的pma在外面忘记删。这其实会打很多人脸,因为大部分人认为这只是个简单的phpmyadmin未授权访问漏洞,并对宝塔进行了一顿diss,没有想到这后面其实是一个复杂的逻辑错误。
其次,用户体验和安全绝对是不冲突的,我十分不喜欢为了保障安全而阉割用户体验的做法。所以希望宝塔官方不会因为这次的漏洞事件而彻底将代码回滚(据说7.4.3的更新只是临时解决方案),该改进的地方还是要改进。
我有数年不再使用Linux面板了,这次也算重新体验了一下2020年的Linux面板,个人感觉宝塔看外在其实是一个比较注重安全的系统,比如自动生成的用户密码、用户名和密码的策略、默认的Php安全配置、自动的版本更新等等,相比于很多国内其他的商业系统,绝对属于有过之而无不及了。但是看代码其实需要改进的地方还有很多,这个以后有机会再细说吧。
本文来自公众号:https://mp.weixin.qq.com/s/3ZjwFo5gWlJACSkeYWQLXA
以上是宝塔面板phpMyAdmin未授权访问安全漏洞是个低级错误吗?的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

表的主键是一列或多列,用于唯一标识表中每条记录。设置主键的步骤如下:登录 phpMyAdmin。选择数据库和表。勾选要作为主键的列。点击 "保存更改"。主键具有数据完整性、查找速度和关系建模方面的好处。

PHPMyAdmin日志文件的默认位置:Linux/Unix/macOS:/var/log/phpmyadminWindows:C:\xampp\phpMyAdmin\logs\日志文件用途:故障排除审计安全性

在 phpMyAdmin 中添加外键可以通过以下步骤实现:选择包含外键的父表。编辑父表结构,在“列”中添加新列。启用外键约束,选择引用表和键。设置更新/删除操作。保存更改。

WordPress 数据库位于 MySQL 数据库中,存储着所有网站数据,可以通过托管提供商的仪表板、FTP 或 phpMyAdmin 访问。数据库名称与网站 URL 或用户名相关,访问需要使用数据库凭据,包括名称、用户名、密码和主机名,这些凭据通常存储在“wp-config.php”文件中。

PHPMyAdmin 的默认用户名和密码为 root 和空。为了安全起见,建议更改默认密码。更改密码的方法:1. 登录 PHPMyAdmin;2. 选择 "privileges";3. 输入新密码并保存。忘记密码时,可通过停止 MySQL 服务并编辑配置文件的方式重置密码:1. 添加 skip-grant-tables 行;2. 登录 MySQL 命令行并重置 root 密码;3. 刷新权限表;4. 删除 skip-grant-tables 行,重启 MySQL 服务。

phpMyAdmin 拒绝访问的原因及解决方案:认证失败:检查用户名和密码是否正确。服务器配置错误:调整防火墙设置,检查数据库端口是否正确。权限问题:授予用户对数据库的访问权限。会话超时:刷新浏览器页面重新连接。phpMyAdmin 配置错误:检查配置文件和文件权限,确保启用了必需的 Apache 模块。服务器问题:等待一段时间后再重试或联系主机提供商。

phpMyAdmin 中删除数据表的步骤:选择数据库和数据表;点击“操作”选项卡;选择“删除”选项;确认并执行删除操作。

可以在 phpMyAdmin 中“结构”选项卡下的“视图”子菜单中找到关联视图。要访问它们,只需选择数据库、点击“结构”选项卡、然后点击“视图”子菜单。
