Introdução
Hoppscotch é um ecossistema de desenvolvimento de API de código aberto, similar ao Postman, com mais de 100.000 usuários mensais. Duas semanas atrás, configuramos uma instância auto-hospedada e executamos nossos agentes de pentest de IA contra ela. Eles encontraram duas vulnerabilidades de alta severidade e uma de média severidade, todas presentes em versões até e incluindo 2026.2.1, e todas corrigidas na versão 2026.3.0:
- Account takeover via redirecionamento aberto que permite que tokens de autenticação sejam exfiltrados para um domínio malicioso, possibilitando que um atacante se autentique como a vítima.
- XSS armazenado via Mock Server, onde JavaScript injetado através de um cabeçalho de resposta é executado no contexto da aplicação Hoppscotch, concedendo a um atacante acesso de leitura e escrita a tudo o que a vítima pode ver.
- Um controle de acesso quebrado no movimento de requisições, onde um usuário de uma equipe pode injetar uma requisição na coleção de outra equipe. Se um membro da equipe alvo a executar, credenciais e chaves de API podem ser exfiltradas.
Todos os três foram divulgados de forma responsável e foram resolvidos.
Remediação
Atualize as instâncias auto-hospedadas do Hoppscotch para a versão 2026.3.0 ou superior.
As vulnerabilidades descobertas foram adicionadas ao banco de dados Aikido Intel:
- https://intel.aikido.dev/cve/AIKIDO-2026-10462
- https://intel.aikido.dev/cve/AIKIDO-2026-10463
- https://intel.aikido.dev/cve/AIKIDO-2026-10461
Redirecionamento aberto resultando em tomada de conta
Alerta: GHSA-7fg7-wx5q-6m3v
CVSS: 8.5 (Alta)
Affected versions: Hoppscotch <= 2026.2.1
Versões corrigidas: 2026.3.0
Hoppscotch possui tanto uma interface web quanto um aplicativo desktop. Para autenticar o aplicativo desktop, uma URL como esta é aberta no navegador:
http://<hoppscotch-instance>/device-login/?redirect_uri=http%3A%2F%2Flocalhost%2Fdevice-token
A instância Hoppscotch usa o parâmetro de consulta redirect_uri para enviar os tokens de sessão. É aqui que reside a vulnerabilidade. O back-end tinha a seguinte validação falha na URI:
if (!redirectUri || !redirectUri.startsWith('http://localhost')) {
throwHTTPErr({
…
})
}
O código verifica se a URI começa com http://localhost, mas não leva em consideração que domínios maliciosos como http://localhost.evil.com também passam por essa verificação. Como resultado, se uma vítima navega para:http://<hoppscotch-instance>/device-login/?redirect_uri=http%3A%2F%2Flocalhost.evil.com%2Fdevice-token
Hoppscotch enviaria seus tokens de sessão para http://localhost.evil.com. Uma vez que localhost é um subdomínio do atacante evil.com domínio, em vez de ir para localhost, a requisição vai para o servidor do atacante. Eles poderiam então usar esses tokens para se autenticar como a vítima.
Nossos agentes encontraram o problema na base de código e verificaram isso configurando um listener em um IP público e criando um payload usando sslip.io. Ao usar a URI http://localhost.<attacker-ip>.sslip.io/, o payload ignorou com sucesso a verificação startsWith enquanto forçava o navegador a resolver o endereço para o servidor do atacante. Uma vez que a vítima se autenticou, os tokens de sessão sensíveis foram vazados diretamente para o nosso listener, confirmando que uma tomada de conta completa era possível.
O seguinte pull request resolveu a vulnerabilidade usando um parser de URL adequado: https://github.com/hoppscotch/hoppscotch/pull/6012
XSS Armazenado via Mock Server
Advisory: GHSA-wj4r-hr4h-g98v
CVSS: 8.5 (High)
Affected versions: Hoppscotch <= 2026.2.1
Patched versions: 2026.3.0
Hoppscotch inclui um recurso de Mock Server que serve respostas definidas pelo usuário a partir de URLs sob /mock/<subdomain>/. O backend está servindo essas respostas da mesma origem que o aplicativo Hoppscotch, o que significa que cross-site scripting (XSS) no MockServer pode ser usado para exfiltrar e modificar dados do usuário.
Para injetar um payload XSS, os agentes de pentest do Aikido contornaram as restrições da UI, enviando uma requisição da API GraphQL que define o Content-Type cabeçalho de resposta para text/html. Isso não era possível via front-end.
Exemplo de corpo da requisição:
{
"query": "mutation($id:ID!,$title:String,$request:String){ updateRESTUserRequest(id:$id,title:$title,request:$request){ id title } }",
"variables": {
"id": "<REQUEST_ID>",
"title": "addPet",
"request": "{\"v\":\"16\",... , \"responses\":{\"XSS\":{\"name\":\"XSS\",\"status\":\"OK\",\"code\":200,\"headers\":[{\"key\":\"content-type\",\"value\":\"text/html\",\"active\":true,\"description\":\"\"}],\"body\":\"<img src=x onerror=\\\"console.log(424212069)\\\">\",\"originalRequest\":{\"v\":\"6\",\"name\":\"xss\",\"method\":\"GET\",\"endpoint\":\"<<mockUrl>>/xss\",\"params\":[],\"headers\":[],\"requestVariables\":[]}}}}"
}
}O XSS é acionado assim que o usuário visita o endpoint da API mock. Por exemplo:
http://<hoppscotch-instance>/mock/-v9juLVaiMnJa/v2/pet/findByStatus

Para testar isso, agentes de IA injetaram um payload console.log no corpo da resposta via API GraphQL, contornando efetivamente as restrições de content-type da UI. Ao navegar para o endpoint mock específico, o navegador executou o script, e os agentes capturaram com sucesso a saída registrada no console.
O seguinte PR resolveu a vulnerabilidade adicionando sanitização e sandboxing com uma Content Security Policy:
https://github.com/hoppscotch/hoppscotch/pull/6006
Controle de Acesso: Mover requisições para outra equipe
Aviso: GHSA-wj4r-hr4h-g98v
CVSS: 6.0 (Médio)
Affected versions: Hoppscotch <= 2026.2.1
Versões corrigidas: 2026.3.0
Hoppscotch permite aos usuários mover ou reordenar requisições entre coleções usando a moveRequest mutação GraphQL. Agentes descobriram que o backend falhou em validar corretamente as permissões para a coleção de destino, permitindo que um atacante "injete" suas próprias requisições em uma coleção pertencente a uma equipe diferente.
A vulnerabilidade existe na lógica de validação do backend: enquanto ele verifica a requisição de origem, ele pula a verificação da equipe de destino se o nextRequestID for definido como nulo. Ao fornecer o destCollID de uma equipe da vítima e deixando o nextRequestID vazio, um atacante pode mover com sucesso uma requisição entre limites de tenant:
{
"operationName": "MoveTeamRequest",
"query": "mutation MoveTeamRequest($req: ID!, $dest: ID!, $next: ID) { moveRequest(requestID: $req, destCollID: $dest, nextRequestID: $next) { id collectionID teamID } }",
"variables": {
"req": "<ATTACKER_REQUEST_ID>",
"dest": "<VICTIM_COLLECTION_ID>",
"next": null
}
}
Isso pode resultar em um impacto na confidencialidade se um membro da equipe da vítima executar, sem saber, a requisição injetada ou a coleção pai. Se isso acontecer, requisições que herdam cabeçalhos de autorização, ou contêm placeholders de variáveis para — por exemplo — chaves de API, os dados sensíveis podem ser exfiltrados para o servidor do atacante.
Abaixo estão os rastros dos agentes identificando a vulnerabilidade:
Examinando relacionamentos de teamID - Estou me perguntando se as requisições podem pertencer a coleções de equipes diferentes sem alterar o teamID. Isso poderia violar a restrição de chave estrangeira se a coleção estiver vinculada a outra equipe. O modelo TeamRequest provavelmente conecta coleções por ID sem impor estritamente que o teamID corresponda a collection.teamID. Em createTeamRequest, eles verificam se o ID da equipe corresponde. No entanto, se mover uma requisição apenas atualiza o collectionID sem alterar o teamID, preciso verificar o esquema Prisma para esclarecer como esses relacionamentos são estruturados. Vamos procurar o esquema no diretório apropriado.
Identificando problemas de controle de acesso - Estou percebendo que o guard verifica apenas se um usuário é membro da equipe associada ao requestID e ignora as permissões para a equipe da coleção de destino. A função moveRequest é protegida por GqlRequestTeamMemberGuard para funções como Editor ou Owner, mas não inclui verificações para GqlCollectionTeamMemberGuard em destCollID. Isso significa que um editor da Equipe A poderia mover uma requisição para a coleção da Equipe B sem ser membro, criando uma séria vulnerabilidade de controle de acesso.
Revisando o tratamento de requisições - Preciso confirmar meu entendimento sobre findRequestAndNextRequest e reorderRequests. Em findRequestAndNextRequest, ele verifica se a requisição existe em srcCollID. Se nextRequestID for fornecido, ele garante que nextRequest pertence à mesma coleção e equipe. No entanto, se nextRequestID for nulo, ele ignora a verificação da propriedade da equipe de destCollID.
Em reorderRequests, ele bloqueia por request.teamID para as coleções de origem e destino. Mas se destCollID for de outra equipe, ele ainda prossegue com as atualizações sem garantir as verificações de equipe adequadas, o que poderia levar a problemas.
O seguinte PR resolveu a vulnerabilidade adicionando uma verificação de autorização:
https://github.com/hoppscotch/hoppscotch/pull/6006
Crédito a TristanInSec, que também identificou independentemente a vulnerabilidade XSS.
Leia mais sobre nossa pesquisa em pentest com IA:
- SvelteSpill: Um bug de Cache Deception em SvelteKit + Vercel
- PromptPwnd: Vulnerabilidades de Prompt Injection em GitHub Actions Usando Agentes de IA
Saiba mais sobre como nossos agentes de pentest com IA funcionam:
- Como tornamos nossos agentes de IA seguros por design
- Como o pentest autônomo se compara ao pentest manual

