Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

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

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
أدوات/GitHubGitHub/renio-wow/cve-2026-51416
التحليل الثابتتحليل الثغرات الأمنيةتحليل الكودأمن الويب
GitHubrenio-wow/cve-2026-51416

CVE-2026-51416

يحلل ثغرة CVE محددة في معالج OAuth الخاص بتطبيق WeChat، ويحدد قراءات استجابات HTTP غير المحدودة التي تؤدي إلى رفض الخدمة، مع إرشادات المعالجة.

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

التحليل: هذا كود إنتاج حي يمكن الوصول إليه من استدعاء GET عام. يوفر ملف تعريف ارتباط الحالة حماية 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)) لاستجابة علوية أخرى.

التحليل: أدرك المشروع بالفعل الحاجة إلى تحديد أحجام أجسام الاستجابات العلوية، ويتم استخدام كل من التطبيقات المخصصة والمشتركة للحدود. يتجاوز مسار 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 تثبت نقاط النهاية العلوية على عناوين 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 و ، لكن الدالة المحددة لا تستخدم أياً منها.

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).
تنزيل الأداة
  • على النقيض، السطر backend/internal/handler/auth_wechat_oauth.go:1149 لا يزال يستخدم io.ReadAll(resp.Body) خاماً.
  • 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.
  • → إيجابي حقيقي