宝塔⾯板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 download.bt/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。
在恢复代码之前,我们先将服务器断⽹,或者将宝塔设置成离线模式:
这么做的⽬的是防⽌宝塔进⾏⾃动版本更新,避免好不容易恢复的代码⼜⾃动升级了。
宝塔系统代码默认安装完是在/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:
...
hod == 'POST':
#登录phpmyadmin
if puri in ['index.php','/index.php']:
content = public.url_encode(_dict())
if not isinstance(content,bytes):
content = de()
<_io = StringIO(content)
username = ('pma_username')
if username:
password = ('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
'''
mysql下载后为什么不是一个安装包
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 = adFile(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(_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链接,会访问到这样⼀个路径:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。