qiqing's Blog.

宝塔低版本登录绕过漏洞

字数统计: 1.3k阅读时长: 4 min
2020/03/16 Share

宝塔低版本登录绕过漏洞

宝塔在6.0版本修改了源代码换成了python的,使用flask框架搭建,其中登录的时候可以选择使用微信扫码登录,在扫描登录的过程存在一处逻辑漏洞可以绕过限制,免密登录,执行恶意代码

分析

BaoTa新版本是7.1,已经将漏洞修复了,先从github上下载6.9.5版本的BaoTa源代码

image-20200210205322222

将项目导入pycharm进行分析

image-20200210214320489

宝塔面板的入口在BT-Panel文件,先跟BT-Panel

image-20200210214519854

从BTPanel模块导入app类,然后,在debug的时候,执行app.run函数,跟进app,发现BTPanel模块就只有一个文件__init__.py文件,跟进这个文件

image-20200210215537228

app是Flask的实例,这里基本可以看出宝塔面板是使用flask框架编写的web程序,针对web程序的审计,一般都是先根据路由跟进业务代码,在宝塔中,所有的路由都在这个文件中定义好了,直接跟进这个文件就好

在我审计宝塔的过程中,我发现宝塔的路由可以分成3部分,第一部分是常规能访问的部分,最简单的就是提示你正确入口,如果有漏洞能在这部分被利用那就非常严重了

image-20200210223012447

第二部分是真正的登录口,或者经过登录口认证后的页面,典型的就是登录入口,宝塔的登录入口是随机8位数,这个登录入口设置成随机8位数,并不是为了防止爆破这么简单,实际上,他是宝塔的第一层认证

image-20200210223334923

这段代码中,login函数就是登录口对应的业务逻辑代码,可以看到这个函数对应的路由有3个,主要是/login和route_path,route_path就是8位随机数,如果我们传入的是/login,最终会返回autterr.html文件,如果传入的是route_path,session[‘admin_auth’]会等于True,这个sessions就是第一层的验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
admin_path = '/'
if os.path.exists(admin_path_file): admin_path = public.readFile(admin_path_file).strip()
......
route_path = os.path.join(admin_path,'')
if route_path[-1] == '/': route_path = route_path[:-1]
if route_path[0] != '/': route_path = '/' + route_path
@app.route('/login',methods=method_all)
@app.route(route_path,methods=method_all)
@app.route(route_path + '/',methods=method_all)
def login():
global admin_check_auth,admin_path,route_path
......
is_auth_path = True
......
if is_auth_path:
if route_path != request.path and route_path + '/' != request.path:
data = {}
data['lan'] = public.getLan('close')
return render_template('autherr.html',data=data)
session['admin_auth'] = True
......

第三部分就是登录进入宝塔面板,一旦进入宝塔面板,这个服务器基本沦陷了,这一部分是主要常规的账号密码登录和扫码登录,常规的密码登录略过,这里主要研究扫码登录

image-20200211095421709

跟进代码的逻辑,这里主要是wxapp这个类来处理登录请求,而从行为上来讲,我们登录需要先获取验证码,所以先跟进fun=login_qrcode时,wxapp类的处理

image-20200211101214116

image-20200211101248312

如果fun=login_qrcode,check函数会直接返回True,进入下个eval函数,执行的代码就是pluwx.login_qrcode(get),跟进login_qrcode函数

image-20200211101643149

login_qrcode在ScanLogin这个类里,wxapp会继承这个类,从这段函数可以看出扫描登录的二维码来自宝塔官方,所以扫描登录会和官方有关系,有了二维码就要扫码登录

扫描登录时,宝塔官方回过来请求你的地址,url为fun=scan_login,这里也会经过check函数

image-20200211103558193

这里逻辑会进入else代码块,这个代码块验证的很严格,没有啥绕过的可能,验证过后进入scan_login函数

image-20200211103804306

主要是写了login.pl文件,这个文件非常好仿造,如果有漏洞能写文件,这里登陆就能绕过了(写shell不更香吗),官方到这里,他的工作就结束了,接下来就是宝塔面板自己的验证过程,宝塔面板需要知道扫描到底成功没有,他的前端会一直访问fun=is_scan_ok

image-20200211105239312

扫描成功之后,他会读取login.pl的内容,返回给前端,前端带入这些参数,访问set_login函数,进行登录跳转

image-20200211105629890

到这里,一个登录的流程就走完了,在这个流程中,is_scan_ok和set_login是公开的,他没有任何认证,这里就会出现一个条件竞争漏洞,如果攻击者能抢先访问到is_scan_ok和set_login函数,那他就能先登录宝塔面板

验证

宝塔面板在登录后有许多操作服务器的功能,这里选择计划任务作为验证漏洞的结果,漏洞利用成功后会使用计划任务反弹一个shell

image-20200211111149388

编写验证的脚本上传另一台vps,这里需要注意cookie,一定要带入宝塔的特定cookie

image-20200211111342549

开启另一台vps上的宝塔

image-20200211111837878

开始验证

image-20200211112722572

反弹成功

修复

宝塔在最新版中修复了这个漏洞,他将原本在第一部分就能访问的is_scan_ok这个路由放到了第二部分,这个漏洞的利用就变得很鸡肋

image-20200211112821650

在宝塔的最新版可以看到不止修复了这个漏洞,在上面还有个修复补丁,可以追踪下上面的修复补丁

CATALOG
  1. 1. 宝塔低版本登录绕过漏洞
    1. 1.0.1. 分析
    2. 1.0.2. 验证
    3. 1.0.3. 修复