Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

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 غير المحدودة التي تؤدي إلى رفض الخدمة، مع إرشادات المعالجة.

عرض المستودع
14منذ 5 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

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

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 المقدمة من المستخدم قبل استدعاء التدفق المحدد.

// 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)
// 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
    }
    ...
}
// 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 تُظهر خطوة المعالجة المسبقة العادية التي تعيّن ملف تعريف ارتباط للحالة وتعيد توجيه المتصفح إلى تدفق OAuth.

التحليل: هذا كود إنتاج حي يمكن الوصول إليه من استدعاء GET عام. يوفر ملف تعريف ارتباط الحالة حماية OAuth-CSRF، لكنه لا يحد من حجم استجابة HTTP التي تصل لاحقاً بعد تبادل الرمز. تم بعد ذلك تتبع سلسلة الاستدعاءات الداخلية وجميع مستدعي نقطة الالتقاط في الإنتاج.

الخطوة 3: تتبع جميع مستدعي الإنتاج إلى exchangeWeChatOAuthCode()

تم تتبع سلسلة الاستدعاءات الداخلية بين المعالج والدوال المساعدة. يصل استدعاء المعالج الرئيسي إلى نقطة الالتقاط من خلال دالة مساعدة (fetchWeChatOAuthIdentity)، بينما يصل استدعاء الدفع إلى نفس نقطة الالتقاط مباشرة.

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)
    ...
}
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 لا يستخدمها.

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
}
// DefaultUpstreamResponseReadMaxBytes is the default read cap for upstream non-streaming response bodies.
const DefaultUpstreamResponseReadMaxBytes int64 = 128 * 1024 * 1024
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
...
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 5<<20))
تنزيل الأداة