Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-51416 — Analizza una specifica CVE nel gestore OAuth di WeChat, identificando letture illimitate delle risposte HTTP che portano a denial of service, con indicazioni di rimedio. | Kitploit
Strumenti/GitHubGitHub/renio-wow/cve-2026-51416
Analisi StaticaAnalisi delle VulnerabilitàAnalisi del CodiceSicurezza Web
GitHubrenio-wow/cve-2026-51416

CVE-2026-51416

Analizza una specifica CVE nel gestore OAuth di WeChat, identificando letture illimitate delle risposte HTTP che portano a denial of service, con indicazioni di rimedio.

Vedi Repository
4 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

1. Riepilogo

  • Tipo di vulnerabilità: Denial of Service / Lettura illimitata della risposta HTTP ([CWE-400])
  • Posizione segnalata: backend/internal/handler/auth_wechat_oauth.go:1149
  • Descrizione della vulnerabilità: Il processo di scambio del token OAuth di WeChat utilizza io.ReadAll(resp.Body) per leggere l'intero corpo della risposta HTTP upstream in memoria senza imporre alcun limite massimo di dimensione prima del buffering.

2. Analisi

Passo 1: Esaminare il sink segnalato in backend/internal/handler/auth_wechat_oauth.go:1124

La funzione segnalata e oltre 50 righe di contesto circostante sono state esaminate. Il sink è l'helper di scambio token exchangeWeChatOAuthCode(). Questa funzione costruisce una richiesta GET verso WeChat, la invia utilizzando un semplice http.Client, e poi legge l'intero corpo della risposta con .

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)
    }
    ...
}

Evidenza:

  • backend/internal/handler/auth_wechat_oauth.go:1142 crea client := &http.Client{Timeout: 30 * time.Second}.
  • backend/internal/handler/auth_wechat_oauth.go:1149 esegue body, err := io.ReadAll(resp.Body).
  • Nessun io.LimitReader, nessun controllo ContentLength e nessuna chiamata a funzione helper è presente prima del buffering del corpo della risposta.

Analisi: L'unica protezione qui è un limite di tempo (Timeout: 30 * time.Second), non un limite di memoria. Un peer remoto può comunque innescare grandi allocazioni di memoria prima che venga raggiunto EOF. È stata poi tracciata la raggiungibilità di questo sink dalle route di produzione.

Passo 2: Tracciare il percorso di richiesta principale dalla route pubblica di callback OAuth

Sono stati esaminati la registrazione delle route di autenticazione e l'handler OAuth di WeChat. La route è registrata nel gruppo pubblico /auth, non in un gruppo protetto da JWT. L'handler di callback riceve i parametri di query code e state forniti dall'utente prima di invocare il flusso segnalato.

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)
}

Evidenza:

  • backend/internal/server/routes/auth.go:27-28 colloca queste route nel gruppo pubblico di router /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 legge code e state dalla richiesta HTTP.
  • backend/internal/handler/auth_wechat_oauth.go:206 chiama fetchWeChatOAuthIdentity(c.Request.Context(), cfg, code).
  • backend/internal/handler/auth_wechat_oauth.go:105-147 mostra il normale passaggio di pre-elaborazione che imposta un cookie di stato e reindirizza il browser nel flusso OAuth.

Analisi: Questo è codice di produzione attivo raggiungibile da un callback GET pubblico. Il cookie di stato fornisce protezione OAuth-CSRF, ma non limita la dimensione della risposta HTTP che arriva a valle dopo lo scambio del token. Sono stati poi seguiti la catena di chiamate interna e tutti i chiamanti di produzione del sink.

Passo 3: Tracciare tutti i chiamanti di produzione in exchangeWeChatOAuthCode()

È stata tracciata la catena di chiamate interna tra l'handler e le funzioni helper. Il callback principale raggiunge il sink tramite un helper (fetchWeChatOAuthIdentity), mentre il callback di pagamento raggiunge lo stesso sink direttamente.

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
}

Evidenza:

  • backend/internal/handler/auth_wechat_oauth.go:1112-1117 mostra fetchWeChatOAuthIdentity() che chiama exchangeWeChatOAuthCode(), poi fetchWeChatUserInfo().
  • backend/internal/handler/auth_wechat_oauth.go:206 è il punto in cui il callback OAuth principale chiama fetchWeChatOAuthIdentity().
  • backend/internal/handler/auth_wechat_oauth.go:438 è il punto in cui il callback OAuth di pagamento chiama exchangeWeChatOAuthCode() direttamente.

Analisi: Il sink segnalato non è codice morto. Una ricerca dei chiamanti a livello di progetto risolve due percorsi di ingresso di produzione nello stesso helper: il callback standard di login/collegamento WeChat e il callback di pagamento WeChat. Sono stati poi verificati eventuali sanitizer, validatori o guardie di dimensione a livello di framework presenti su questo percorso di risposta.

Passo 4: Verificare i limiti di dimensione e i pattern di sicurezza esistenti nel codebase

Il codebase è stato cercato per pattern di limitazione della dimensione del corpo della risposta, ed è stato esaminato l'helper condiviso già utilizzato altrove per letture upstream limitate. Tale helper utilizza io.LimitReader(..., maxBytes+1) e genera un errore esplicitamente quando il limite viene superato, ma il codice OAuth di WeChat non lo utilizza.

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))

Evidenza:

  • backend/internal/service/upstream_response_limit.go:26-40 definisce readUpstreamResponseBodyLimited() utilizzando io.LimitReader(reader, maxBytes+1).
  • backend/internal/service/upstream_response_limit.go:49-61 definisce ReadUpstreamResponseBody() come wrapper condiviso di lettura limitata.
  • backend/internal/config/config.go:55-58 definisce DefaultUpstreamResponseReadMaxBytes.
  • backend/internal/service/crs_sync_service.go:1166 utilizza io.ReadAll(io.LimitReader(resp.Body, 1<<20)) per leggere una risposta upstream.
  • backend/internal/service/crs_sync_service.go:1201 utilizza io.ReadAll(io.LimitReader(resp.Body, 5<<20)) per un'altra risposta upstream.
  • Al contrario, backend/internal/handler/auth_wechat_oauth.go:1149 utilizza ancora un io.ReadAll(resp.Body) grezzo.

Analisi: Il progetto ha già riconosciuto la necessità di limitare le dimensioni dei corpi delle risposte upstream, e sia le implementazioni di limite ad hoc che quelle condivise sono in uso. Il percorso OAuth di WeChat segnalato bypassa completamente queste protezioni. È stato poi verificato l'endpoint remoto per determinare se è fisso e se ciò cambia la conclusione.

Passo 5: Verificare i confini di fiducia e il contesto di codice adiacente

Sono state esaminate le definizioni delle costanti e il codice adiacente di recupero informazioni utente. L'endpoint del token OAuth è hardcoded su WeChat e non è fornito dall'utente, ma il contenuto del corpo della risposta è comunque dati di rete remoti e viene letto senza alcun limite di dimensione. Una seconda lettura upstream illimitata esiste nello stesso file per la richiesta di informazioni utente.

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
    }
}

Evidenza:

  • backend/internal/handler/auth_wechat_oauth.go:52-54 hardcoda gli endpoint upstream agli URL di WeChat.
  • backend/internal/handler/auth_wechat_oauth.go:1197 esegue un secondo io.ReadAll(resp.Body) illimitato all'interno di fetchWeChatUserInfo().
  • backend/internal/server/middleware/backend_mode_guard.go:38-55 consente esplicitamente /auth/oauth/wechat/callback e /auth/oauth/wechat/payment/callback anche quando la modalità backend è abilitata.

Analisi: Il nome host WeChat fisso riduce il controllo di un attaccante sulla selezione dell'endpoint, ma non aggiunge alcun limite di dimensione della risposta. La debolezza rimane perché l'applicazione bufferizza byte arbitrari di risposta remota in memoria senza un limite superiore. Questo è codice di handler di produzione, non codice di test, demo o morto.

Percorso decisionale

  • D1: Il codice esegue una lettura illimitata di dati forniti da remoto? → Sì. exchangeWeChatOAuthCode() emette una richiesta HTTP in uscita e poi chiama io.ReadAll(resp.Body) in backend/internal/handler/auth_wechat_oauth.go:1142-1149 senza alcuna guardia di dimensione.
  • D2: Il sink è raggiungibile da un percorso di richiesta di produzione reale? → Sì. backend/internal/server/routes/auth.go:73 e backend/internal/server/routes/auth.go:80 registrano le route pubbliche di start/callback WeChat, e backend/internal/handler/auth_wechat_oauth.go:206 instrada il flusso di callback attraverso fetchWeChatOAuthIdentity().
  • D3: Esistono sanitizer, validatori o funzioni framework/helper che impongono una dimensione massima del corpo della risposta prima della lettura? → No. L'helper di limite condiviso esiste in backend/internal/service/upstream_response_limit.go:26-40 e altri servizi utilizzano io.LimitReader in backend/internal/service/crs_sync_service.go:1166 e backend/internal/service/crs_sync_service.go:1201, ma la funzione segnalata non ne utilizza nessuno.
  • D4: Il codice è morto, solo di test o altrimenti escluso dalla produzione? → No. Il codice risiede nel file di handler di produzione backend/internal/handler/auth_wechat_oauth.go, è registrato in backend/internal/server/routes/auth.go:73-82, e il middleware di modalità backend consente ancora i percorsi di callback in backend/internal/server/middleware/backend_mode_guard.go:38-55.
  • → Vero Positivo

3. Conclusione

Vulnerabilità Vera

Evidenza chiave:

  • backend/internal/handler/auth_wechat_oauth.go:1149 utilizza body, err := io.ReadAll(resp.Body) per leggere l'intera risposta del token upstream dopo aver impostato solo un timeout — nessun limite di dimensione.
  • backend/internal/server/routes/auth.go:73-82 espone /oauth/wechat/callback e /oauth/wechat/payment/callback, ed entrambe le route raggiungono l'helper segnalato tramite 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 mostrano che il codebase utilizza già io.LimitReader per letture upstream limitate — il codice OAuth di WeChat semplicemente non lo fa.

4. Rimedio

  • Aggiungere una dimensione massima esplicita del corpo della risposta prima del buffering della risposta del token WeChat — ad esempio, avvolgere resp.Body con io.LimitReader(..., maxBytes+1) e rifiutare i corpi che superano il limite.
  • Applicare la stessa correzione alla lettura adiacente delle informazioni utente in backend/internal/handler/auth_wechat_oauth.go:1197, che presenta lo stesso identico pattern illimitato.
  • Preferire il riutilizzo del pattern di lettura limitata esistente da backend/internal/service/upstream_response_limit.go:26-40 affinché il flusso OAuth sia coerente con il modo in cui il resto del codebase gestisce le risposte upstream.
  • Dopo la correzione, riverificare entrambi i percorsi di chiamata di produzione: il callback OAuth principale (backend/internal/handler/auth_wechat_oauth.go:206) e il callback di pagamento (backend/internal/handler/auth_wechat_oauth.go:438).
Scarica lo strumento