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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-28378 — إثبات مفهوم وتحليل مفصل لـ CVE-2021-28378، وهي ثغرة XSS مخزنة في Gitea تتيح حقن التعليمات البرمجية التعسفي وتصعيد الامتيازات وتنفيذ التعليمات البرمجية عن بُعد عبر خطافات git. | Kitploit
أدوات/GitHubGitHub/pandatix/cve-2021-28378
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليمتطوير الحمولات
GitHubpandatix/cve-2021-28378

CVE-2021-28378

إثبات مفهوم وتحليل مفصل لـ CVE-2021-28378، وهي ثغرة XSS مخزنة في Gitea تتيح حقن التعليمات البرمجية التعسفي وتصعيد الامتيازات وتنفيذ التعليمات البرمجية عن بُعد عبر خطافات git.

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

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2021-28378

تفاصيل حول هذا CVE هنا.

يستهدف هذا CVE غياب تهريب السلاسل (string escaping) في جانب العميل للمحتوى الذي يتم جلبه من الخادم. فهو يمكّن المهاجم من حقن كود تعسفي بسهولة، عن طريق إنشاء تعليق على issue أو pull request. تم تقديم هذا في commit 7d7ab1eeae43d99fe329878ac9c8db5e45e2dee5، في نهاية يناير 2020، ولكن بسبب squash لا يمكننا معرفة من فعل ذلك بالضبط للتحقيق في أخطاء مماثلة تم تقديمها.

أولاً، دعنا نفهم من أين تأتي هذه الثغرة. لهذا، دعنا نرى الـ commit الذي يصلح هذا CVE.

قبل:

  • أولاً:
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
  • ثانياً:
    html: `
<div>
    <p><small>${issue.repository.full_name} on ${createdAt}</small></p>
    <p><span class="${color}">${svg(octicon)}</span> <strong>${issue.title}</strong> #${index}</p>
    <p>${body}</p>
    ${labels}
</div>
`

يمكننا أن نرى أنه لا يوجد أي تهريب HTML على label.name وissue.repository.full_name وissue.title وbody، وهي سلاسل يمكن للمستخدم التلاعب بها، وبالتالي حقن كود تعسفي. لاحظ أن issue.repository.full_name لا يمكن التلاعب به فعلياً لحقن كود لأنه يجب أن يتبع قواعد كثيرة جداً ([\w._-]+) لا يمكننا تجاوزها مع هذا CVE. هذا بالضبط ما يصلحه الـ PR عن طريق تحويلها إلى htmlEscape.

بعد:

  • أولاً:
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${htmlEscape(label.name)}</div>`;
  • ثانياً:
    html: `
<div>
    <p><small>${htmlEscape(issue.repository.full_name)} on ${createdAt}</small></p>
    <p><span class="${color}">${svg(octicon)}</span> <strong>${htmlEscape(issue.title)}</strong> #${index}</p>
    <p>${htmlEscape(body)}</p>
    ${labels}
</div>
`

فهم الثغرة

دعنا نلقي نظرة سريعة على الملف web_src/js/features/contextpopup.js، لمعرفة كيفية تشغيل CVE.

...
const {AppSubUrl} = window.config;

export default function initContextPopups() {
  const refIssues = $('.ref-issue');
  if (!refIssues.length) return;

  refIssues.each(function () {
    const [index, _issues, repo, owner] = $(this).attr('href').replace(/[#?].*$/, '').split('/').reverse();
    issuePopup(owner, repo, index, $(this));
  });
}

function issuePopup(owner, repo, index, $element) {
  $.get(`${AppSubUrl}/api/v1/repos/${owner}/${repo}/issues/${index}`, (issue) => {
...
    for (let i = 0; i < issue.labels.length; i++) {
...
      labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
    }
...

    $element.popup({
      variation: 'wide',
      delay: {
        show: 250
      },
      html: `
<div>
  <p><small>${issue.repository.full_name} on ${createdAt}</small></p>
  <p><span class="${color}">${svg(octicon, 16)}</span> <strong>${issue.title}</strong> #${index}</p>
  <p>${body}</p>
  ${labels}
</div>
`
    });
  });
}

ما نراه هنا هو أنه لكل عقدة تحمل الكلاس ref-issue، سنضيف popup ضعيف. الآن، نحتاج إلى إيجاد أين يتم إرسال ref-issue إلى المستخدم: دعنا نستخدم grep (ونحتفظ فقط بالنتائج المهمة، أي ليس ملفات الاختبار أو ما شابهها)! رقم الـ commit التالي يقابل وسم 1.12.4، وهو الإصدار الافتراضي من ملف docker-compose.yml الرسمي، المتأثر بهذا CVE.

cd gitea
git checkout 8a51c48eb6367513e0518bcd412e64f6cbfe5e1a
grep -r ref-issue

modules/markup/html.go:		replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))
modules/markup/html.go:		replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
modules/markup/html.go:		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), reftext, "ref-issue")
modules/markup/html.go:			link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], path, ref.Issue), reftext, "ref-issue")
modules/markup/html.go:			link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
modules/markup/sanitizer.go:	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`ref-issue`)).OnElements("a")

نعلم الآن أنه لا يوجد محتوى ثابت يحمل الكلاس ref-issue، بل يتم إنشاؤه ديناميكياً. النتيجة الأخيرة ليست مهمة لأنها مجرد قاعدة sanitizer policy. أما البقية فمهمة، وسنرى السبب.

دعنا نتعمق في modules/markup/html.go.

func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
	if ctx.metas == nil {
		return
	}
	m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
	if m == nil {
		return
	}
	link := node.Data[m[0]:m[1]]
	id := "#" + node.Data[m[2]:m[3]]

	// extract repo and org name from matched link like
	// http://localhost:3000/gituser/myrepo/issues/1
	linkParts := strings.Split(path.Clean(link), "/")
	matchOrg := linkParts[len(linkParts)-4]
	matchRepo := linkParts[len(linkParts)-3]

	if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
		// TODO if m[4]:m[5] is not nil, then link is to a comment,
		// and we should indicate that in the text somehow
		replaceContent(node, m[0], m[1], createLink(link, id, "ref-issue"))

	} else {
		orgRepoID := matchOrg + "/" + matchRepo + id
		replaceContent(node, m[0], m[1], createLink(link, orgRepoID, "ref-issue"))
	}
}

سنحتاج أيضاً إلى فهم ما يفعله getIssueFullPattern.

func getIssueFullPattern() *regexp.Regexp {
	if issueFullPattern == nil {
		appURL := setting.AppURL
		if len(appURL) > 0 && appURL[len(appURL)-1] != '/' {
			appURL += "/"
		}
		issueFullPattern = regexp.MustCompile(appURL +
			`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
	}
	return issueFullPattern
}

دعنا نلخص لفهم المهمة المنجزة هناك.

في fullIssuePatternProcessor، نحتاج أولاً إلى التأكد من أن ctx.metas ليس nil (وهو ما نفترض أنه صحيح لأنه مبني بواسطة دوال/طرق أعلى في مكدس الاستدعاء). ثم نتحقق مما إذا كانت بيانات العقدة تطابق مساراً مثل http://mygitea.com/gituser/myrepo/issues/1. إذا كان هناك تطابق، نستبدله برابط. إذا كان الرابط يتعلق بنفس المستخدم والمستودع، نستبدله بنمط #<issue_id>، وإلا بـ <user>/<repo>#<issue_id>. في كلتا الحالتين، لدينا كلاس ref-issue يُضاف لتوليد الـ popup.

نتائج grep الأخرى تقوم بنفس النوع من العمل.

وقت اللعب!

الآن لدينا السياق، والذي يدور حول استبدال رابط مثل http://mygitea.com/gituser/myrepo/issues/1 برابط أقصر، يمكننا استنتاج أين تتم هذه المهمة: في issues و PR، حيث يمكنك إضافة label و/أو نشر تعليق.

لهذا، لنفترض أن لديك مستودعاً بصلاحيات كتابة.

Label

انتقل إلى مستودعك، وأنشئ label جديد باسم يحتوي على كود تعسفي (مثل <script>alert('label.name')</script>).

Issue

انتقل إلى issues مستودعك، وأنشئ issue جديد بكود تعسفي في العنوان والنص، وأضف إليه الـ label السابق.

لننفذ بعض الكود

أنشئ issue جديد في أي مستودع، وأضف في نصه رابطاً إلى الـ issue المحقون لديك مثل http://mygitea.com/gituser/myrepo/issues/1. سيتم استبداله بالرابط الذي رأيناه سابقاً، وعندما يمرر شخص ما مؤشر الفأرة فوق الرابط، سيتم تنفيذ الكود التعسفي: سيتم عرض popup يضم label.name.

وقت الاستغلال

ثبت أن CVE-2021-28378 قادر على تمكين حقن كود تعسفي. دعنا نتخيل ما يمكن أن يفعله المهاجم.

في Gitea، لديك رمز CSRF يؤكد هويتك عند ملء النماذج و/أو استدعاء API، مما يمنع مشاكل أمنية مثل تضمين iframe من Gitea لسرقة وصولاتك. رمز CSRF مخزّن في ملفات تعريف الارتباط الخاصة بك (_csrf)، وكذلك في معظم صفحات الويب (باستثناء بعضها مثل /api/v1/swagger) التي يزورها المستخدم ضمن بعض الحقول... وهكذا في صفحات issues/PR. الحقل الأكثر إثارة للاهتمام الذي يحتوي عليه موجود داخل الجزء المشترك من ترويسة صفحات HTML، دائمًا بنفس XPath: /html/head/meta[9].

بهذه البيانات، يمكننا الآن إرسال إجراءات موثقة من أي مستخدم متأثر بحمولتنا، بمجرد الحصول على رمز CSRF في JS، باستخدام ما يلي: (document.evaluate("/html/head/meta[9]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue).content.

تنزيل الأداة