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-51416 — Analisa um CVE específico no manipulador OAuth do WeChat, identificando leituras ilimitadas de respostas HTTP que levam à negação de serviço, com orientação de remediação. | Kitploit
Ferramentas/GitHubGitHub/renio-wow/cve-2026-51416
Análise EstáticaAnálise de VulnerabilidadesAnálise de CódigoSegurança Web
GitHubrenio-wow/cve-2026-51416

CVE-2026-51416

Analisa um CVE específico no manipulador OAuth do WeChat, identificando leituras ilimitadas de respostas HTTP que levam à negação de serviço, com orientação de remediação.

Ver Repositório
3há 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

1. Resumo

  • Tipo de Vulnerabilidade: Negação de Serviço / Leitura Ilimitada de Resposta HTTP ([CWE-400])
  • Local Sinalizado: backend/internal/handler/auth_wechat_oauth.go:1149
  • Descrição da Vulnerabilidade: O processo de troca de token OAuth do WeChat usa io.ReadAll(resp.Body) para ler todo o corpo da resposta HTTP upstream na memória sem impor nenhum limite máximo de tamanho antes do buffer.

2. Análise

Etapa 1: Examinar o sink sinalizado em backend/internal/handler/auth_wechat_oauth.go:1124

A função sinalizada e mais de 50 linhas de contexto circundante foram revisadas. O sink é o helper de troca de token exchangeWeChatOAuthCode(). Esta função constrói uma requisição GET para o WeChat, a envia usando um http.Client simples e então lê todo o corpo da resposta com .

io.ReadAll(resp.Body)
root@kitploit:~
func exchangeWeChatOAuthCode(ctx context.Context, cfg wechatOAuthConfig, code string) (*wechatOAuthTokenResponse, error) {
    endpoint, err := url.Parse(wechatOAuthAccessTokenURL)
    ...
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
    ...
    client := &http.Client{Timeout: 30 * time.Second}
    resp, err := client.Do(req)
    ...
    defer func() { _ = resp.Body.Close() }()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("read wechat access token response: %w", err)
    }
    if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
        return nil, fmt.Errorf("wechat access token status=%d", resp.StatusCode)
    }
    ...
}

Evidências:

  • backend/internal/handler/auth_wechat_oauth.go:1142 cria client := &http.Client{Timeout: 30 * time.Second}.
  • backend/internal/handler/auth_wechat_oauth.go:1149 executa body, err := io.ReadAll(resp.Body).
  • Nenhum io.LimitReader, nenhuma verificação de ContentLength e nenhuma chamada de função helper está presente antes do buffer do corpo da resposta.

Análise: A única proteção aqui é um limite de tempo (Timeout: 30 * time.Second), não um limite de memória. Um peer remoto ainda pode acionar grandes alocações de memória antes que o EOF seja alcançado. A acessibilidade deste sink a partir de rotas de produção foi então rastreada.

Etapa 2: Rastrear o caminho principal da requisição a partir da rota pública de callback OAuth

O registro da rota de autenticação e o handler OAuth do WeChat foram revisados. A rota está registrada no grupo público /auth, não em um grupo protegido por JWT. O handler de callback recebe os parâmetros de consulta code e state fornecidos pelo usuário antes de invocar o fluxo sinalizado.

root@kitploit:~
// routes
auth := v1.Group("/auth")
auth.Use(servermiddleware.BackendModeAuthGuard(settingService))
...
auth.GET("/oauth/wechat/start", h.Auth.WeChatOAuthStart)
auth.GET("/oauth/wechat/callback", h.Auth.WeChatOAuthCallback)
root@kitploit:~
// callback handler
func (h *AuthHandler) WeChatOAuthCallback(c *gin.Context) {
    ...
    code := strings.TrimSpace(c.Query("code"))
    state := strings.TrimSpace(c.Query("state"))
    if code == "" || state == "" {
        redirectOAuthError(c, frontendCallback, "missing_params", "missing code/state", "")
        return
    }
    ...
    tokenResp, userInfo, err := fetchWeChatOAuthIdentity(c.Request.Context(), cfg, code)
    if err != nil {
        redirectOAuthError(c, frontendCallback, "provider_error", "wechat_identity_fetch_failed", singleLine(err.Error()))
        return
    }
    ...
}
root@kitploit:~
// start handler
func (h *AuthHandler) WeChatOAuthStart(c *gin.Context) {
    ...
    state, err := oauth.GenerateState()
    ...
    wechatSetCookie(c, wechatOAuthStateCookieName, encodeCookieValue(state), wechatOAuthCookieMaxAgeSec, secureCookie)
    ...
    c.Redirect(http.StatusFound, authURL)
}

Evidências:

  • backend/internal/server/routes/auth.go:27-28 coloca essas rotas no grupo público de roteadores /auth.
  • backend/internal/server/routes/auth.go:73 registra auth.GET("/oauth/wechat/start", h.Auth.WeChatOAuthStart).
  • backend/internal/server/routes/auth.go:80 registra auth.GET("/oauth/wechat/callback", h.Auth.WeChatOAuthCallback).
  • backend/internal/handler/auth_wechat_oauth.go:160-162 lê code e state da requisição HTTP.
  • backend/internal/handler/auth_wechat_oauth.go:206 chama fetchWeChatOAuthIdentity(c.Request.Context(), cfg, code).
  • backend/internal/handler/auth_wechat_oauth.go:105-147 mostra a etapa de pré-processamento normal que define um cookie de estado e redireciona o navegador para o fluxo OAuth.

Análise: Este é código de produção ativo, acessível a partir de um callback GET público. O cookie de estado fornece proteção OAuth-CSRF, mas não limita o tamanho da resposta HTTP que chega a jusante após a troca de token. A cadeia de chamadas interna e todos os chamadores de produção do sink foram então acompanhados.

Etapa 3: Rastrear todos os chamadores de produção em exchangeWeChatOAuthCode()

A cadeia de chamadas interna entre o handler e as funções helper foi rastreada. O callback principal alcança o sink através de um helper (fetchWeChatOAuthIdentity), enquanto o callback de pagamento alcança o mesmo sink diretamente.

root@kitploit:~
func fetchWeChatOAuthIdentity(ctx context.Context, cfg wechatOAuthConfig, code string) (*wechatOAuthTokenResponse, *wechatOAuthUserInfoResponse, error) {
    tokenResp, err := exchangeWeChatOAuthCode(ctx, cfg, code)
    if err != nil {
        return nil, nil, err
    }
    userInfo, err := fetchWeChatUserInfo(ctx, tokenResp)
    ...
}
root@kitploit:~
cfg, err := h.getWeChatOAuthConfig(c.Request.Context(), "mp", c)
...
cfg.redirectURI = h.resolveWeChatPaymentOAuthCallbackURL(c.Request.Context(), c)
tokenResp, err := exchangeWeChatOAuthCode(c.Request.Context(), cfg, code)
if err != nil {
    redirectOAuthError(c, frontendCallback, "token_exchange_failed", "failed to exchange oauth code", err.Error())
    return
}

Evidências:

  • backend/internal/handler/auth_wechat_oauth.go:1112-1117 mostra fetchWeChatOAuthIdentity() chamando exchangeWeChatOAuthCode() e depois fetchWeChatUserInfo().
  • backend/internal/handler/auth_wechat_oauth.go:206 é onde o callback OAuth principal chama fetchWeChatOAuthIdentity().
  • backend/internal/handler/auth_wechat_oauth.go:438 é onde o callback OAuth de pagamento chama exchangeWeChatOAuthCode() diretamente.

Análise: O sink sinalizado não é código morto. Uma busca de chamadores em todo o projeto resolve dois caminhos de entrada de produção para o mesmo helper: o callback padrão de login/vínculo do WeChat e o callback de pagamento do WeChat. Quaisquer sanitizadores, validadores ou proteções de tamanho em nível de framework presentes neste caminho de resposta foram então verificados.

Etapa 4: Verificar limites de tamanho e padrões de segurança existentes no código

O código foi pesquisado por padrões de limitação de tamanho do corpo da resposta, e o helper compartilhado já usado em outros lugares para leituras upstream limitadas foi revisado. Esse helper usa io.LimitReader(..., maxBytes+1) e gera erro explicitamente quando o limite é excedido, mas o código OAuth do WeChat não o usa.

root@kitploit:~
func readUpstreamResponseBodyLimited(reader io.Reader, maxBytes int64) ([]byte, error) {
    ...
    body, err := io.ReadAll(io.LimitReader(reader, maxBytes+1))
    if err != nil {
        return nil, err
    }
    if int64(len(body)) > maxBytes {
        return nil, fmt.Errorf("%w: limit=%d", ErrUpstreamResponseBodyTooLarge, maxBytes)
    }
    return body, nil
}
root@kitploit:~
// DefaultUpstreamResponseReadMaxBytes is the default read cap for upstream non-streaming response bodies.
const DefaultUpstreamResponseReadMaxBytes int64 = 128 * 1024 * 1024
root@kitploit:~
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
...
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 5<<20))

Evidências:

  • backend/internal/service/upstream_response_limit.go:26-40 define readUpstreamResponseBodyLimited() usando io.LimitReader(reader, maxBytes+1).
  • backend/internal/service/upstream_response_limit.go:49-61 define ReadUpstreamResponseBody() como um wrapper compartilhado de leitura limitada.
  • backend/internal/config/config.go:55-58 define DefaultUpstreamResponseReadMaxBytes.
  • backend/internal/service/crs_sync_service.go:1166 usa io.ReadAll(io.LimitReader(resp.Body, 1<<20)) para ler uma resposta upstream.
  • backend/internal/service/crs_sync_service.go:1201 usa io.ReadAll(io.LimitReader(resp.Body, 5<<20)) para outra resposta upstream.
  • Em contraste, backend/internal/handler/auth_wechat_oauth.go:1149 ainda usa um io.ReadAll(resp.Body) bruto.

Análise: O projeto já reconheceu a necessidade de limitar os tamanhos dos corpos de resposta upstream, e tanto implementações de limite ad-hoc quanto compartilhadas estão em uso. O caminho OAuth do WeChat sinalizado ignora essas proteções completamente. O endpoint remoto foi então verificado para determinar se é fixo e se isso altera a conclusão.

Etapa 5: Verificar limites de confiança e contexto de código adjacente

As definições de constantes e o código adjacente de busca de informações do usuário foram revisados. O endpoint de token OAuth é codificado para o WeChat e não é fornecido pelo usuário, mas o conteúdo do corpo da resposta ainda é dados de rede remotos e é lido sem nenhum limite de tamanho. Uma segunda leitura upstream ilimitada existe no mesmo arquivo para a requisição de informações do usuário.

root@kitploit:~
var (
    wechatOAuthAccessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token"
    wechatOAuthUserInfoURL    = "https://api.weixin.qq.com/sns/userinfo"
)
root@kitploit:~
body, err := io.ReadAll(resp.Body)
if err != nil {
    return nil, fmt.Errorf("read wechat userinfo response: %w", err)
}
root@kitploit:~
for _, suffix := range []string{
    "/auth/oauth/linuxdo/callback",
    "/auth/oauth/wechat/callback",
    "/auth/oauth/wechat/payment/callback",
    ...
} {
    if strings.HasSuffix(path, suffix) {
        return true
    }
}

Evidências:

  • backend/internal/handler/auth_wechat_oauth.go:52-54 codifica endpoints upstream para URLs do WeChat.
  • backend/internal/handler/auth_wechat_oauth.go:1197 realiza uma segunda leitura io.ReadAll(resp.Body) ilimitada dentro de fetchWeChatUserInfo().
  • backend/internal/server/middleware/backend_mode_guard.go:38-55 permite explicitamente /auth/oauth/wechat/callback e /auth/oauth/wechat/payment/callback mesmo quando o modo backend está habilitado.

Análise: O hostname fixo do WeChat reduz o controle de um atacante sobre a seleção de endpoints, mas não adiciona nenhum limite de tamanho de resposta. A fraqueza permanece porque a aplicação armazena em buffer bytes arbitrários de resposta remota na memória sem um limite superior. Este é código de handler de produção, não código de teste, demonstração ou morto.

Caminho de Decisão

  • P1: O código realiza uma leitura ilimitada de dados fornecidos remotamente? → Sim. exchangeWeChatOAuthCode() emite uma requisição HTTP de saída e então chama io.ReadAll(resp.Body) em backend/internal/handler/auth_wechat_oauth.go:1142-1149 sem proteção de tamanho.
  • P2: O sink é acessível a partir de um caminho real de requisição de produção? → Sim. backend/internal/server/routes/auth.go:73 e backend/internal/server/routes/auth.go:80 registram as rotas públicas de início/callback do WeChat, e backend/internal/handler/auth_wechat_oauth.go:206 direciona o fluxo de callback através de fetchWeChatOAuthIdentity().
  • P3: Existem sanitizadores, validadores ou funções de framework/helper que impõem um tamanho máximo de corpo de resposta antes da leitura? → Não. O helper de limite compartilhado existe em backend/internal/service/upstream_response_limit.go:26-40 e outros serviços usam io.LimitReader em backend/internal/service/crs_sync_service.go:1166 e backend/internal/service/crs_sync_service.go:1201, mas a função sinalizada não usa nenhum deles.
  • P4: O código é morto, apenas de teste ou excluído da produção? → Não. O código reside no arquivo de handler de produção backend/internal/handler/auth_wechat_oauth.go, está registrado em backend/internal/server/routes/auth.go:73-82, e o middleware de modo backend ainda permite os caminhos de callback em backend/internal/server/middleware/backend_mode_guard.go:38-55.
  • → Verdadeiro Positivo

3. Conclusão

Vulnerabilidade Verdadeira

Evidências-Chave:

  • backend/internal/handler/auth_wechat_oauth.go:1149 usa body, err := io.ReadAll(resp.Body) para ler toda a resposta de token upstream após definir apenas um timeout — sem limite de tamanho.
  • backend/internal/server/routes/auth.go:73-82 expõe /oauth/wechat/callback e /oauth/wechat/payment/callback, e ambas as rotas alcançam o helper sinalizado via backend/internal/handler/auth_wechat_oauth.go:206 e backend/internal/handler/auth_wechat_oauth.go:438.
  • backend/internal/service/upstream_response_limit.go:26-40 e backend/internal/service/crs_sync_service.go:1166 mostram que o código já usa io.LimitReader para leituras upstream limitadas — o código OAuth do WeChat simplesmente não usa.

4. Remediação

  • Adicione um tamanho máximo explícito de corpo de resposta antes do buffer da resposta de token do WeChat — por exemplo, envolva resp.Body com io.LimitReader(..., maxBytes+1) e rejeite corpos que excedam o limite.
  • Aplique a mesma correção à leitura adjacente de informações do usuário em backend/internal/handler/auth_wechat_oauth.go:1197, que tem o padrão ilimitado idêntico.
  • Prefira reutilizar o padrão de leitura limitada existente de backend/internal/service/upstream_response_limit.go:26-40 para que o fluxo OAuth seja consistente com a forma como o restante do código lida com respostas upstream.
  • Após a correção, re-verifique ambos os caminhos de chamada de produção: o callback OAuth principal (backend/internal/handler/auth_wechat_oauth.go:206) e o callback de pagamento (backend/internal/handler/auth_wechat_oauth.go:438).
Baixar ferramenta