Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-51416 — Анализирует конкретную CVE в обработчике OAuth WeChat, выявляя неограниченное чтение HTTP-ответов, приводящее к отказу в обслуживании, с рекомендациями по устранению. | Kitploit
Инструменты/GitHubGitHub/renio-wow/cve-2026-51416
Статический анализАнализ уязвимостейАнализ КодаВеб-безопасность
GitHubrenio-wow/cve-2026-51416

CVE-2026-51416

Анализирует конкретную CVE в обработчике OAuth WeChat, выявляя неограниченное чтение HTTP-ответов, приводящее к отказу в обслуживании, с рекомендациями по устранению.

Репозиторий
4 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

1. Сводка

  • Тип уязвимости: Отказ в обслуживании / Неограниченное чтение HTTP-ответа ([CWE-400])
  • Место обнаружения: backend/internal/handler/auth_wechat_oauth.go:1149
  • Описание уязвимости: В процессе обмена OAuth-токена WeChat используется io.ReadAll(resp.Body) для чтения полного тела HTTP-ответа вышестоящего сервиса в память без установления какого-либо максимального ограничения размера перед буферизацией.

2. Анализ

Шаг 1: Изучение отмеченного места сбоя в backend/internal/handler/auth_wechat_oauth.go:1124

Были проверены отмеченная функция и более 50 строк окружающего контекста. Местом сбоя является вспомогательная функция обмена токена exchangeWeChatOAuthCode(). Эта функция формирует GET-запрос к WeChat, отправляет его с помощью обычного http.Client, а затем считывает всё тело ответа с помощью .

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

Доказательства:

  • backend/internal/handler/auth_wechat_oauth.go:1142 создаёт client := &http.Client{Timeout: 30 * time.Second}.
  • backend/internal/handler/auth_wechat_oauth.go:1149 выполняет body, err := io.ReadAll(resp.Body).
  • Никакого io.LimitReader, проверки ContentLength или вызова вспомогательной функции перед буферизацией тела ответа нет.

Анализ: Единственной защитой здесь является ограничение по времени (Timeout: 30 * time.Second), а не ограничение памяти. Удалённый узел всё ещё может вызвать большие выделения памяти до достижения EOF. Затем была прослежена достижимость этого места сбоя из производственных маршрутов.

Шаг 2: Прослеживание основного пути запроса от публичного маршрута обратного вызова OAuth

Были проверены регистрация маршрутов аутентификации и обработчик WeChat OAuth. Маршрут зарегистрирован в публичной группе /auth, а не в группе, защищённой JWT. Обработчик обратного вызова получает предоставленные пользователем параметры запроса code и state перед вызовом отмеченного потока.

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

Доказательства:

  • backend/internal/server/routes/auth.go:27-28 размещает эти маршруты в публичной группе маршрутизатора /auth.
  • backend/internal/server/routes/auth.go:73 регистрирует auth.GET("/oauth/wechat/start", h.Auth.WeChatOAuthStart).
  • backend/internal/server/routes/auth.go:80 регистрирует auth.GET("/oauth/wechat/callback", h.Auth.WeChatOAuthCallback).
  • backend/internal/handler/auth_wechat_oauth.go:160-162 считывает code и state из HTTP-запроса.
  • backend/internal/handler/auth_wechat_oauth.go:206 вызывает fetchWeChatOAuthIdentity(c.Request.Context(), cfg, code).
  • backend/internal/handler/auth_wechat_oauth.go:105-147 показывает обычный этап предварительной обработки, который устанавливает cookie состояния и перенаправляет браузер в поток OAuth.

Анализ: Это рабочий производственный код, доступный через публичный GET-обратный вызов. Cookie состояния обеспечивает защиту от OAuth-CSRF, но не ограничивает размер HTTP-ответа, который поступает ниже по потоку после обмена токена. Затем были прослежены внутренняя цепочка вызовов и все производственные вызывающие объекты места сбоя.

Шаг 3: Прослеживание всех производственных вызывающих объектов в exchangeWeChatOAuthCode()

Была прослежена внутренняя цепочка вызовов между обработчиком и вспомогательными функциями. Основной обратный вызов достигает места сбоя через вспомогательную функцию (fetchWeChatOAuthIdentity), в то время как платёжный обратный вызов достигает того же места сбоя напрямую.

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
}

Доказательства:

  • backend/internal/handler/auth_wechat_oauth.go:1112-1117 показывает, что fetchWeChatOAuthIdentity() вызывает exchangeWeChatOAuthCode(), а затем fetchWeChatUserInfo().
  • backend/internal/handler/auth_wechat_oauth.go:206 — это место, где основной обратный вызов OAuth вызывает fetchWeChatOAuthIdentity().
  • backend/internal/handler/auth_wechat_oauth.go:438 — это место, где платёжный обратный вызов OAuth вызывает exchangeWeChatOAuthCode() напрямую.

Анализ: Отмеченное место сбоя не является мёртвым кодом. Поиск вызывающих объектов по всему проекту выявляет два производственных пути входа в одну и ту же вспомогательную функцию: стандартный обратный вызов входа/привязки WeChat и платёжный обратный вызов WeChat. Затем были проверены любые санитайзеры, валидаторы или защитные механизмы размера на уровне фреймворка, присутствующие на этом пути ответа.

Шаг 4: Проверка ограничений размера и существующих шаблонов безопасности в кодовой базе

Была выполнена проверка кодовой базы на предмет шаблонов ограничения размера тела ответа, а также проверена общая вспомогательная функция, уже используемая в других местах для ограниченного чтения вышестоящих данных. Эта вспомогательная функция использует io.LimitReader(..., maxBytes+1) и явно сообщает об ошибке при превышении лимита, но код WeChat OAuth её не использует.

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

Доказательства:

  • backend/internal/service/upstream_response_limit.go:26-40 определяет readUpstreamResponseBodyLimited() с использованием io.LimitReader(reader, maxBytes+1).
  • backend/internal/service/upstream_response_limit.go:49-61 определяет ReadUpstreamResponseBody() как общую обёртку ограниченного чтения.
  • backend/internal/config/config.go:55-58 определяет DefaultUpstreamResponseReadMaxBytes.
  • backend/internal/service/crs_sync_service.go:1166 использует io.ReadAll(io.LimitReader(resp.Body, 1<<20)) для чтения ответа вышестоящего сервиса.
  • backend/internal/service/crs_sync_service.go:1201 использует io.ReadAll(io.LimitReader(resp.Body, 5<<20)) для другого ответа вышестоящего сервиса.
  • Напротив, backend/internal/handler/auth_wechat_oauth.go:1149 по-прежнему использует сырой io.ReadAll(resp.Body).

Анализ: Проект уже признал необходимость ограничения размера тел ответов вышестоящих сервисов, и используются как специальные, так и общие реализации ограничения. Отмеченный путь WeChat OAuth полностью обходит эти защиты. Затем была проверена удалённая конечная точка, чтобы определить, является ли она фиксированной и меняет ли это вывод.

Шаг 5: Проверка границ доверия и соседнего контекста кода

Были проверены определения констант и соседний код получения информации о пользователе. Конечная точка OAuth-токена жёстко задана для WeChat и не предоставляется пользователем, но содержимое тела ответа по-прежнему является удалёнными сетевыми данными и считывается без какого-либо ограничения размера. В том же файле существует второе неограниченное чтение вышестоящих данных для запроса информации о пользователе.

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

Доказательства:

  • backend/internal/handler/auth_wechat_oauth.go:52-54 жёстко задаёт вышестоящие конечные точки как URL-адреса WeChat.
  • backend/internal/handler/auth_wechat_oauth.go:1197 выполняет второе неограниченное io.ReadAll(resp.Body) внутри fetchWeChatUserInfo().
  • backend/internal/server/middleware/backend_mode_guard.go:38-55 явно разрешает /auth/oauth/wechat/callback и /auth/oauth/wechat/payment/callback, даже когда включён режим бэкенда.

Анализ: Фиксированное имя хоста WeChat снижает контроль злоумышленника над выбором конечной точки, но не добавляет никакого ограничения размера ответа. Слабость сохраняется, поскольку приложение буферизует произвольные удалённые байты ответа в память без верхней границы. Это производственный код обработчика, а не тестовый, демонстрационный или мёртвый код.

Путь принятия решения

  • В1: Выполняет ли код неограниченное чтение удалённо предоставленных данных? → Да. exchangeWeChatOAuthCode() отправляет исходящий HTTP-запрос, а затем вызывает io.ReadAll(resp.Body) в backend/internal/handler/auth_wechat_oauth.go:1142-1149 без защиты по размеру.
  • В2: Достижимо ли место сбоя из реального производственного пути запроса? → Да. backend/internal/server/routes/auth.go:73 и backend/internal/server/routes/auth.go:80 регистрируют публичные маршруты запуска/обратного вызова WeChat, а backend/internal/handler/auth_wechat_oauth.go:206 направляет поток обратного вызова через fetchWeChatOAuthIdentity().
  • В3: Существуют ли какие-либо санитайзеры, валидаторы или вспомогательные функции фреймворка, обеспечивающие максимальный размер тела ответа перед чтением? → Нет. Общая вспомогательная функция ограничения существует в backend/internal/service/upstream_response_limit.go:26-40, а другие сервисы используют io.LimitReader в backend/internal/service/crs_sync_service.go:1166 и backend/internal/service/crs_sync_service.go:1201, но отмеченная функция не использует ни одну из них.
  • В4: Является ли код мёртвым, тестовым или иным образом исключённым из производства? → Нет. Код находится в производственном файле обработчика backend/internal/handler/auth_wechat_oauth.go, зарегистрирован в backend/internal/server/routes/auth.go:73-82, а промежуточное ПО режима бэкенда по-прежнему разрешает пути обратного вызова в backend/internal/server/middleware/backend_mode_guard.go:38-55.
  • → Истинно положительный результат

3. Заключение

Истинная уязвимость

Ключевые доказательства:

  • backend/internal/handler/auth_wechat_oauth.go:1149 использует body, err := io.ReadAll(resp.Body) для чтения полного ответа вышестоящего токена после установки только тайм-аута — без ограничения размера.
  • backend/internal/server/routes/auth.go:73-82 открывает /oauth/wechat/callback и /oauth/wechat/payment/callback, и оба маршрута достигают отмеченной вспомогательной функции через backend/internal/handler/auth_wechat_oauth.go:206 и backend/internal/handler/auth_wechat_oauth.go:438.
  • backend/internal/service/upstream_response_limit.go:26-40 и backend/internal/service/crs_sync_service.go:1166 показывают, что кодовая база уже использует io.LimitReader для ограниченного чтения вышестоящих данных — код WeChat OAuth просто этого не делает.

4. Исправление

  • Добавьте явный максимальный размер тела ответа перед буферизацией ответа токена WeChat — например, оберните resp.Body с помощью io.LimitReader(..., maxBytes+1) и отклоняйте тела, превышающие лимит.
  • Примените то же исправление к соседнему чтению информации о пользователе в backend/internal/handler/auth_wechat_oauth.go:1197, где используется идентичный неограниченный шаблон.
  • Предпочтительно повторно использовать существующий шаблон ограниченного чтения из backend/internal/service/upstream_response_limit.go:26-40, чтобы поток OAuth был согласован с тем, как остальная часть кодовой базы обрабатывает ответы вышестоящих сервисов.
  • После исправления повторно проверьте оба производственных пути вызова: основной обратный вызов OAuth (backend/internal/handler/auth_wechat_oauth.go:206) и платёжный обратный вызов (backend/internal/handler/auth_wechat_oauth.go:438).
Скачать инструмент