Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2021-28378 — Preuve de concept détaillée et analyse de CVE-2021-28378, une vulnérabilité XSS stockée dans Gitea permettant l'injection de code arbitraire, l'élévation de privilèges et l'exécution de code à distance via les hooks git. | Kitploit
Outils/GitHubGitHub/pandatix/cve-2021-28378
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationDéveloppement de Charges Utiles
GitHubpandatix/cve-2021-28378

CVE-2021-28378

Preuve de concept détaillée et analyse de CVE-2021-28378, une vulnérabilité XSS stockée dans Gitea permettant l'injection de code arbitraire, l'élévation de privilèges et l'exécution de code à distance via les hooks git.

Voir le dépôt
414il y a 8 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2021-28378

Détails sur cette CVE ici.

Cette CVE cible un manque d'échappement de chaînes côté client pour du contenu récupéré depuis le serveur. Elle permet à un attaquant d'injecter facilement du code arbitraire en créant un commentaire sur un ticket ou une pull request. Cela a été introduit dans le commit 7d7ab1eeae43d99fe329878ac9c8db5e45e2dee5, à la fin de janvier 2020, mais en raison d'un squash, on ne peut pas savoir exactement qui a fait cela pour enquêter sur des bugs similaires introduits.

Tout d'abord, comprenons d'où vient cette vulnérabilité. Pour cela, regardons le commit qui corrige cette CVE.

Avant :

  • Premièrement :
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${label.name}</div>`;
  • Deuxièmement :
    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>
`

On voit qu'il n'y a pas d'échappement HTML sur label.name, issue.repository.full_name, issue.title et body, qui sont des chaînes qu'un utilisateur peut manipuler, et donc injecter du code arbitraire. Notez que issue.repository.full_name ne peut pas vraiment être manipulé pour injecter du code car il doit suivre trop de règles ([\w._-]+) que nous ne pouvons pas contourner avec cette CVE. C'est exactement ce que la PR corrige en les passant dans htmlEscape.

Après :

  • Premièrement :
    labels += `<div class="ui label" style="color: ${color}; background-color:#${label.color};">${htmlEscape(label.name)}</div>`;
  • Deuxièmement :
    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>
`

Comprendre la vulnérabilité

Jetons un coup d'œil rapide au fichier web_src/js/features/contextpopup.js pour comprendre comment déclencher la 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>
`
    });
  });
}

Ce que nous voyons là, c'est que pour chaque nœud avec la classe ref-issue, nous ajoutons une popup vulnérable. Maintenant, trouvons où un ref-issue est envoyé à l'utilisateur : faisons un grep (et gardons seulement les résultats intéressants, donc pas les fichiers de test ou similaires) ! Le numéro de commit suivant correspond au tag 1.12.4, la version par défaut du fichier officiel docker-compose.yml, qui est affectée par cette 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")

Nous savons maintenant qu'il n'y a pas de contenu statique avec la classe ref-issue, mais qu'il est généré dynamiquement. La dernière ligne n'est pas intéressante car c'est juste une règle de politique de sanitizer. Les autres sont intéressantes, nous verrons pourquoi.

Plongeons dans 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"))
	}
}

Nous devons aussi comprendre ce que fait 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
}

Résumons pour comprendre le travail effectué ici.

Dans fullIssuePatternProcessor, nous devons d'abord être sûrs que ctx.metas n'est pas nil (ce que nous supposons être vrai car il est construit par des fonctions/méthodes de niveau supérieur dans la pile d'appels). Ensuite, nous vérifions si les données du nœud correspondent à un chemin comme http://mygitea.com/gituser/myrepo/issues/1. Si c'est le cas, nous le remplaçons par un lien. Si le lien concerne le même utilisateur et le même dépôt, on le remplace par le motif #<id_issue>, sinon par <utilisateur>/<dépôt>#<id_issue>. Dans ces deux cas, une classe ref-issue est ajoutée pour générer la popup.

Les autres résultats du grep font à peu près la même chose.

Il est temps de jouer !

Maintenant que nous avons le contexte, qui consiste à remplacer un lien comme http://mygitea.com/gituser/myrepo/issues/1 par un lien plus court, nous pouvons déduire où ce travail est effectué : dans les tickets et les PR, où vous pouvez ajouter un label et/ou poster un commentaire.

Pour cela, supposons que vous ayez un dépôt avec des droits d'écriture.

Label

Allez dans votre dépôt, et créez un nouveau label avec un nom contenant du code arbitraire (comme <script>alert('label.name')</script>).

Ticket

Allez dans les tickets de votre dépôt, créez un nouveau ticket avec du code arbitraire dans le titre et le corps, et ajoutez-lui le label précédent.

Exécutons du code

Télécharger l’outil